@dative-gpi/foundation-shared-components 0.0.205 → 0.0.207

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/assets/images/map/imagery.png +0 -0
  2. package/assets/images/map/osm.png +0 -0
  3. package/components/FSCalendar.vue +1 -0
  4. package/components/FSCalendarTwin.vue +2 -1
  5. package/components/FSClock.vue +18 -6
  6. package/components/FSImageCard.vue +72 -0
  7. package/components/autocompletes/FSAutoCompleteAddress.vue +6 -6
  8. package/components/autocompletes/FSAutocompleteLanguage.vue +4 -5
  9. package/components/autocompletes/FSAutocompleteTimeZone.vue +4 -5
  10. package/components/fields/FSColorField.vue +9 -11
  11. package/components/fields/FSDateField.vue +10 -4
  12. package/components/fields/FSDateRangeField.vue +10 -4
  13. package/components/fields/FSDateTimeField.vue +20 -11
  14. package/components/fields/FSDateTimeRangeField.vue +35 -25
  15. package/components/fields/FSRichTextField.vue +132 -54
  16. package/components/fields/FSTermField.vue +190 -186
  17. package/components/fields/FSTimeField.vue +17 -12
  18. package/components/fields/FSTranslateField.vue +7 -14
  19. package/components/fields/FSTranslateRichTextField.vue +185 -0
  20. package/components/map/FSMap.vue +129 -65
  21. package/components/map/FSMapEditPointAddressOverlay.vue +19 -18
  22. package/components/map/FSMapLayerButton.vue +71 -0
  23. package/models/map.ts +1 -0
  24. package/models/richTextVariable.ts +5 -0
  25. package/models/variableNode.ts +105 -0
  26. package/package.json +4 -4
  27. package/styles/components/fs_image_card.scss +18 -0
  28. package/styles/components/fs_map.scss +10 -14
  29. package/styles/components/fs_rich_text_field.scss +16 -4
  30. package/styles/components/index.scss +1 -0
  31. package/utils/lexical.ts +2 -0
@@ -115,6 +115,37 @@
115
115
  >
116
116
  mdi-link
117
117
  </FSIcon>
118
+ <v-menu
119
+ v-if="$props.variableReferences && $props.variableReferences.length > 0"
120
+ :closeOnContentClick="false"
121
+ v-model="menuVariable"
122
+ >
123
+ <template
124
+ v-slot:activator="{ props }"
125
+ >
126
+ <FSIcon
127
+ v-bind="props"
128
+ class="fs-rich-text-field-icon"
129
+ :color="toolbarColors.variable"
130
+ :style="style"
131
+ >
132
+ mdi-variable
133
+ </FSIcon>
134
+ </template>
135
+ <FSCard
136
+ padding="12"
137
+ width="300px"
138
+ :elevation="true"
139
+ >
140
+ <FSAutoCompleteField
141
+ itemTitle="code"
142
+ :placeholder="$tr('ui.rich-text-field.variable-placeholder', 'Choose a variable...')"
143
+ :items="$props.variableReferences"
144
+ :returnObject="true"
145
+ @update:modelValue="insertVariable($event)"
146
+ />
147
+ </FSCard>
148
+ </v-menu>
118
149
  <v-divider
119
150
  vertical
120
151
  />
@@ -150,10 +181,21 @@
150
181
  </FSRow>
151
182
  <div
152
183
  class="fs-rich-text-field"
153
- :id="id"
154
184
  :style="style"
155
- :contenteditable="!readonly && $props.editable"
156
- />
185
+ >
186
+ <div
187
+ class="fs-rich-text-field-content"
188
+ :data-variable-values="JSON.stringify($props.variableValues)"
189
+ :contenteditable="!readonly && $props.editable"
190
+ :data-readonly="$props.variant === 'readonly'"
191
+ :id="id"
192
+ />
193
+ <slot
194
+ name="append-inner"
195
+ v-bind="{ props: $props }"
196
+ />
197
+ </div>
198
+
157
199
  <FSTextField
158
200
  v-if="isLink && !readonly && $props.editable"
159
201
  :hideHeader="true"
@@ -176,31 +218,35 @@
176
218
  </template>
177
219
 
178
220
  <script lang="ts">
179
- import type { ElementNode} from "lexical";
180
- import { $createParagraphNode, $getSelection, $isElementNode, $isRangeSelection, $setSelection, CAN_UNDO_COMMAND, createEditor, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, ParagraphNode, UNDO_COMMAND } from "lexical";
181
- import type { HeadingTagType} from "@lexical/rich-text";
182
- import { $createHeadingNode, HeadingNode, registerRichText } from "@lexical/rich-text";
221
+ import { computed, defineComponent, onMounted, type PropType, ref, watch } from "vue";
222
+
223
+ import { $createParagraphNode, $getSelection, $isElementNode, $isRangeSelection, $isNodeSelection, $setSelection, CAN_UNDO_COMMAND, createEditor, type ElementNode, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, ParagraphNode, UNDO_COMMAND } from "lexical";
224
+ import { $createHeadingNode, HeadingNode, type HeadingTagType, registerRichText } from "@lexical/rich-text";
183
225
  import { createEmptyHistoryState, registerHistory } from "@lexical/history";
184
- import type { PropType} from "vue";
185
- import { computed, defineComponent, onMounted, ref, watch } from "vue";
186
226
  import { $createLinkNode, $isLinkNode, LinkNode } from "@lexical/link";
187
227
  import { $wrapNodes } from "@lexical/selection";
188
228
 
229
+ import { emptyLexicalState, getAncestor, getSelectedNode } from "@dative-gpi/foundation-shared-components/utils";
189
230
  import { useBreakpoints, useColors } from "@dative-gpi/foundation-shared-components/composables";
190
- import { getAncestor, getSelectedNode } from "@dative-gpi/foundation-shared-components/utils";
191
- import type { ColorBase} from "@dative-gpi/foundation-shared-components/models";
192
- import { ColorEnum } from "@dative-gpi/foundation-shared-components/models";
231
+ import { type ColorBase, ColorEnum } from "@dative-gpi/foundation-shared-components/models";
232
+
233
+ import { $createVariableNode, $isVariableNode, VariableNode } from "../../models/variableNode";
234
+ import { type RichTextVariable } from "../../models/richTextVariable";
193
235
 
236
+ import FSAutoCompleteField from "./FSAutocompleteField.vue";
194
237
  import FSTextField from "./FSTextField.vue";
195
238
  import FSIcon from "../FSIcon.vue";
239
+ import FSCard from "../FSCard.vue";
196
240
  import FSCol from "../FSCol.vue";
197
241
  import FSRow from "../FSRow.vue";
198
242
 
199
243
  export default defineComponent({
200
244
  name: "FSRichTextField",
201
245
  components: {
246
+ FSAutoCompleteField,
202
247
  FSTextField,
203
248
  FSIcon,
249
+ FSCard,
204
250
  FSCol,
205
251
  FSRow
206
252
  },
@@ -240,6 +286,15 @@ export default defineComponent({
240
286
  required: false,
241
287
  default: "standard"
242
288
  },
289
+ variableReferences: {
290
+ type: Array as PropType<Array<RichTextVariable>>,
291
+ default: () => []
292
+ },
293
+ variableValues: {
294
+ type: Object,
295
+ required: false,
296
+ default: () => ({})
297
+ },
243
298
  editable: {
244
299
  type: Boolean,
245
300
  required: false,
@@ -251,7 +306,7 @@ export default defineComponent({
251
306
  const { isMobileSized } = useBreakpoints();
252
307
  const { getColors } = useColors();
253
308
 
254
- const linkColors = computed(()=> getColors(props.linkColor));
309
+ const linkColors = computed(() => getColors(props.linkColor));
255
310
  const lights = getColors(ColorEnum.Light);
256
311
  const darks = getColors(ColorEnum.Dark);
257
312
 
@@ -261,13 +316,13 @@ export default defineComponent({
261
316
  const isItalic = ref(false);
262
317
  const isUnderline = ref(false);
263
318
  const isStrikethrough = ref(false);
319
+ const isVariable = ref(false);
320
+ const menuVariable = ref(false);
264
321
 
265
322
  const id = `${Math.random()}-editor`;
266
- const emptyState = "{\"root\":{\"children\":[{\"children\":[],\"direction\":null,\"format\":\"\",\"indent\":0,\"type\":\"paragraph\",\"version\":1}],\"direction\":null,\"format\":\"\",\"indent\":0,\"type\":\"root\",\"version\":1}}";
267
-
268
323
 
269
324
  const linkUrl = ref("https://");
270
-
325
+
271
326
  const config = {
272
327
  namespace: "MyEditor",
273
328
  theme: {
@@ -289,7 +344,8 @@ export default defineComponent({
289
344
  nodes: [
290
345
  HeadingNode,
291
346
  LinkNode,
292
- ParagraphNode
347
+ ParagraphNode,
348
+ VariableNode
293
349
  ],
294
350
  onError: console.error
295
351
  }
@@ -309,7 +365,7 @@ export default defineComponent({
309
365
  }
310
366
  else {
311
367
  editor.update((): void => {
312
- editor.setEditorState(editor.parseEditorState(emptyState));
368
+ editor.setEditorState(editor.parseEditorState(emptyLexicalState));
313
369
  });
314
370
  }
315
371
  });
@@ -318,7 +374,7 @@ export default defineComponent({
318
374
  return ["readonly"].includes(props.variant);
319
375
  });
320
376
 
321
- const style = computed((): { [key: string] : string | null | undefined } => {
377
+ const style = computed((): { [key: string]: string | null | undefined } => {
322
378
  let minHeight: string | undefined = "auto";
323
379
  if (!readonly.value) {
324
380
  const base = isMobileSized.value ? 30 : 42;
@@ -335,38 +391,40 @@ export default defineComponent({
335
391
  case "standard": {
336
392
  if (!props.editable) {
337
393
  return {
338
- "--fs-rich-text-field-undo-cursor" : "default",
339
- "--fs-rich-text-field-icon-cursor" : "default",
340
- "--fs-rich-text-field-border-color" : lights.base,
341
- "--fs-rich-text-field-color" : lights.dark,
394
+ "--fs-rich-text-field-undo-cursor": "default",
395
+ "--fs-rich-text-field-icon-cursor": "default",
396
+ "--fs-rich-text-field-border-color": lights.base,
397
+ "--fs-rich-text-field-color": lights.dark,
342
398
  "--fs-rich-text-field-active-border-color": lights.base,
343
- "--fs-rich-text-field-link-color" : linkColors.value.light,
344
- "--fs-rich-text-field-min-height" : minHeight
399
+ "--fs-rich-text-field-link-color": linkColors.value.light,
400
+ "--fs-rich-text-field-min-height": minHeight
345
401
  };
346
402
  }
347
403
  else {
348
404
  return {
349
- "--fs-rich-text-field-undo-cursor" : canUndo.value ? "pointer" : "default",
350
- "--fs-rich-text-field-icon-cursor" : "pointer",
351
- "--fs-rich-text-field-border-color" : lights.dark,
352
- "--fs-rich-text-field-color" : darks.base,
405
+ "--fs-rich-text-field-undo-cursor": canUndo.value ? "pointer" : "default",
406
+ "--fs-rich-text-field-icon-cursor": "pointer",
407
+ "--fs-rich-text-field-border-color": lights.dark,
408
+ "--fs-rich-text-field-color": darks.base,
353
409
  "--fs-rich-text-field-active-border-color": darks.dark,
354
- "--fs-rich-text-field-link-color" : linkColors.value.dark,
355
- "--fs-rich-text-field-min-height" : minHeight
410
+ "--fs-rich-text-field-link-color": linkColors.value.dark,
411
+ "--fs-rich-text-field-min-height": minHeight,
412
+ "--fs-rich-text-field-variable-backgroundcolor": getColors(ColorEnum.Primary).light,
413
+ "--fs-rich-text-field-variable-color": getColors(ColorEnum.Primary).lightContrast
356
414
  };
357
415
  }
358
416
  }
359
417
  case "readonly": return {
360
- "--fs-rich-text-field-border-color" : "transparent",
361
- "--fs-rich-text-field-color" : darks.base,
418
+ "--fs-rich-text-field-border-color": "transparent",
419
+ "--fs-rich-text-field-color": darks.base,
362
420
  "--fs-rich-text-field-active-border-color": "transparent",
363
- "--fs-rich-text-field-link-color" : linkColors.value.dark,
364
- "--fs-rich-text-field-min-height" : minHeight
421
+ "--fs-rich-text-field-link-color": linkColors.value.dark,
422
+ "--fs-rich-text-field-min-height": minHeight
365
423
  }
366
424
  }
367
425
  });
368
426
 
369
- const toolbarColors = computed((): {[code: string]: string} => {
427
+ const toolbarColors = computed((): { [code: string]: string } => {
370
428
  if (props.editable) {
371
429
  return {
372
430
  undo: canUndo.value ? darks.base : lights.base,
@@ -374,7 +432,8 @@ export default defineComponent({
374
432
  italic: isItalic.value ? darks.base : lights.base,
375
433
  underline: isUnderline.value ? darks.base : lights.base,
376
434
  strikethrough: isStrikethrough.value ? darks.base : lights.base,
377
- link: isLink.value ? darks.base : lights.base
435
+ link: isLink.value ? darks.base : lights.base,
436
+ variable: isVariable.value ? darks.base : lights.base
378
437
  };
379
438
  }
380
439
  else {
@@ -391,6 +450,7 @@ export default defineComponent({
391
450
 
392
451
  const updateToolbar = (): void => {
393
452
  const selection = $getSelection();
453
+ isVariable.value = false;
394
454
  if ($isRangeSelection(selection)) {
395
455
  isBold.value = selection.hasFormat("bold");
396
456
  isItalic.value = selection.hasFormat("italic");
@@ -398,12 +458,17 @@ export default defineComponent({
398
458
  isStrikethrough.value = selection.hasFormat("strikethrough");
399
459
  isLink.value = $isLinkNode(getSelectedNode(selection)) || $isLinkNode(getSelectedNode(selection).getParent());
400
460
  }
461
+ else if($isNodeSelection(selection)){
462
+ if($isVariableNode(selection?.getNodes()[0])){
463
+ isVariable.value = true;
464
+ }
465
+ }
401
466
  };
402
467
 
403
468
  editor.registerUpdateListener(({ editorState }) => {
404
469
  editorState.read(() => {
405
470
  updateToolbar();
406
- if (JSON.stringify(editorState.toJSON()) !== emptyState) {
471
+ if (JSON.stringify(editorState.toJSON()) !== emptyLexicalState) {
407
472
  emit("update:modelValue", JSON.stringify(editorState.toJSON()));
408
473
  }
409
474
  else {
@@ -437,6 +502,17 @@ export default defineComponent({
437
502
  });
438
503
  };
439
504
 
505
+ const insertVariable = (variable: { code: string; defaultValue: any; type: string }) => {
506
+ menuVariable.value = false;
507
+ editor.update(() => {
508
+ const selection = $getSelection();
509
+ if ($isRangeSelection(selection)) {
510
+ const variableNode = $createVariableNode(variable.code, variable.defaultValue, variable.type);
511
+ selection.insertNodes([variableNode]);
512
+ }
513
+ });
514
+ };
515
+
440
516
  const openLink = (): void => {
441
517
  if (!isLink.value) {
442
518
  isLink.value = true;
@@ -448,7 +524,7 @@ export default defineComponent({
448
524
  if ($isRangeSelection(selection)) {
449
525
  toggleLink();
450
526
  }
451
- });
527
+ });
452
528
  }
453
529
  };
454
530
 
@@ -498,7 +574,7 @@ export default defineComponent({
498
574
  let linkNode: LinkNode | null = null;
499
575
  nodes.forEach((node) => {
500
576
  const parent = node.getParent();
501
- if ( parent === linkNode || parent === null || ($isElementNode(node) && !node.isInline())) {
577
+ if (parent === linkNode || parent === null || ($isElementNode(node) && !node.isInline())) {
502
578
  return;
503
579
  }
504
580
  if ($isLinkNode(parent)) {
@@ -517,7 +593,7 @@ export default defineComponent({
517
593
  }
518
594
  if (!parent.is(prevParent)) {
519
595
  prevParent = parent;
520
- linkNode = $createLinkNode(linkUrl.value, {rel, target, title});
596
+ linkNode = $createLinkNode(linkUrl.value, { rel, target, title });
521
597
 
522
598
  if ($isLinkNode(parent)) {
523
599
  if (node.getPreviousSibling() === null) {
@@ -562,30 +638,32 @@ export default defineComponent({
562
638
  editor.setEditorState(editor.parseEditorState(props.modelValue!));
563
639
  });
564
640
  }
565
- else if (JSON.stringify(editor.getEditorState().toJSON()) !== emptyState) {
641
+ else if (JSON.stringify(editor.getEditorState().toJSON()) !== emptyLexicalState) {
566
642
  editor.update(() => {
567
- editor.setEditorState(editor.parseEditorState(emptyState));
643
+ editor.setEditorState(editor.parseEditorState(emptyLexicalState));
568
644
  });
569
645
  }
570
646
  }
571
647
  });
572
648
 
573
649
  return {
650
+ FORMAT_ELEMENT_COMMAND,
651
+ FORMAT_TEXT_COMMAND,
652
+ toolbarColors,
653
+ menuVariable,
654
+ UNDO_COMMAND,
574
655
  readonly,
575
- style,
576
- id,
656
+ linkUrl,
577
657
  editor,
578
658
  isLink,
579
- linkUrl,
580
- toolbarColors,
581
- openLink,
582
- toggleLink,
583
- formatText,
659
+ style,
660
+ id,
584
661
  formatParagraph,
585
- UNDO_COMMAND,
586
- FORMAT_TEXT_COMMAND,
587
- FORMAT_ELEMENT_COMMAND,
588
- }
662
+ insertVariable,
663
+ formatText,
664
+ toggleLink,
665
+ openLink
666
+ };
589
667
  }
590
668
  });
591
669
  </script>