@blokkli/editor 2.0.0-alpha.61 → 2.0.0-alpha.63

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 (128) hide show
  1. package/dist/global/types/colorOptions.d.ts +16 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +168 -14
  4. package/dist/modules/agent/runtime/app/components/Conversation/Item/Assistant/index.vue +14 -5
  5. package/dist/modules/agent/runtime/app/features/agent/ConversationsAdmin/ConversationsTab/Item.vue +2 -2
  6. package/dist/modules/agent/runtime/app/features/agent/ConversationsAdmin/RatingsTab/Item.vue +2 -2
  7. package/dist/modules/agent/runtime/app/features/agent/ConversationsAdmin/SplitView/ConversationDetail.vue +2 -2
  8. package/dist/modules/agent/runtime/app/features/agent/Transcript/MessageContent.vue +14 -1
  9. package/dist/modules/agent/runtime/app/features/agent/Transcript/index.vue +30 -12
  10. package/dist/modules/agent/runtime/app/helpers/linkifyBlockUuids.d.ts +11 -0
  11. package/dist/modules/agent/runtime/app/helpers/linkifyBlockUuids.js +47 -0
  12. package/dist/modules/agent/runtime/app/tools/auto_translate_paragraphs/Component.d.vue.ts +22 -0
  13. package/dist/modules/agent/runtime/app/tools/auto_translate_paragraphs/Component.vue +28 -26
  14. package/dist/modules/agent/runtime/app/tools/auto_translate_paragraphs/Component.vue.d.ts +22 -0
  15. package/dist/modules/agent/runtime/app/tools/auto_translate_paragraphs/index.d.ts +15 -0
  16. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.d.vue.ts +22 -0
  17. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.vue +59 -16
  18. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/Component.vue.d.ts +22 -0
  19. package/dist/modules/agent/runtime/app/tools/delegate_text_rewrite/index.d.ts +15 -0
  20. package/dist/modules/agent/runtime/app/tools/fieldDiffApproval.d.ts +85 -15
  21. package/dist/modules/agent/runtime/app/tools/fieldDiffApproval.js +138 -28
  22. package/dist/modules/agent/runtime/app/tools/schemas.d.ts +15 -5
  23. package/dist/modules/agent/runtime/app/tools/schemas.js +29 -12
  24. package/dist/modules/agent/runtime/app/tools/search_text/index.js +1 -1
  25. package/dist/modules/agent/runtime/app/tools/update_text_fields/Component.d.vue.ts +22 -0
  26. package/dist/modules/agent/runtime/app/tools/update_text_fields/Component.vue +54 -30
  27. package/dist/modules/agent/runtime/app/tools/update_text_fields/Component.vue.d.ts +22 -0
  28. package/dist/modules/agent/runtime/app/tools/update_text_fields/index.d.ts +15 -0
  29. package/dist/modules/agent/runtime/server/default-system-prompts/architecture.js +1 -1
  30. package/dist/modules/agent/runtime/server/default-system-prompts/important-rules.js +1 -0
  31. package/dist/modules/charts/runtime/blokkli/tools/chart_schemas.d.ts +1 -1
  32. package/dist/modules/charts/runtime/blokkli/tools/chart_schemas.js +12 -5
  33. package/dist/modules/charts/runtime/components/ChartRenderer/index.vue +7 -16
  34. package/dist/modules/charts/runtime/features/charts/Editor/ColorDropdown/index.vue +7 -77
  35. package/dist/modules/charts/runtime/features/charts/Editor/CsvImport/csvHelpers.d.ts +1 -1
  36. package/dist/modules/charts/runtime/features/charts/Editor/useChartEditorState.d.ts +1 -1
  37. package/dist/modules/charts/runtime/helpers/index.d.ts +2 -1
  38. package/dist/modules/charts/runtime/helpers/index.js +4 -2
  39. package/dist/modules/drupal/graphql/base/fragment.paragraphsBlokkliMutationResult.graphql +3 -0
  40. package/dist/modules/drupal/runtime/adapter/index.js +38 -27
  41. package/dist/runtime/composables/useBlokkliRuntimeConfig.d.ts +50 -0
  42. package/dist/runtime/composables/useBlokkliRuntimeConfig.js +34 -0
  43. package/dist/runtime/editor/adapter/index.d.ts +2 -1
  44. package/dist/runtime/editor/components/ColorDropdown/index.d.vue.ts +26 -0
  45. package/dist/runtime/editor/components/ColorDropdown/index.vue +116 -0
  46. package/dist/runtime/editor/components/ColorDropdown/index.vue.d.ts +26 -0
  47. package/dist/runtime/editor/components/DiffApproval/Highlight/Item.d.vue.ts +14 -20
  48. package/dist/runtime/editor/components/DiffApproval/Highlight/Item.vue +97 -52
  49. package/dist/runtime/editor/components/DiffApproval/Highlight/Item.vue.d.ts +14 -20
  50. package/dist/runtime/editor/components/DiffApproval/Highlight/index.d.vue.ts +6 -6
  51. package/dist/runtime/editor/components/DiffApproval/Highlight/index.vue +30 -21
  52. package/dist/runtime/editor/components/DiffApproval/Highlight/index.vue.d.ts +6 -6
  53. package/dist/runtime/editor/components/DiffApproval/Toolbar/index.d.vue.ts +19 -9
  54. package/dist/runtime/editor/components/DiffApproval/Toolbar/index.vue +18 -9
  55. package/dist/runtime/editor/components/DiffApproval/Toolbar/index.vue.d.ts +19 -9
  56. package/dist/runtime/editor/components/DiffApproval/index.d.vue.ts +14 -6
  57. package/dist/runtime/editor/components/DiffApproval/index.vue +62 -35
  58. package/dist/runtime/editor/components/DiffApproval/index.vue.d.ts +14 -6
  59. package/dist/runtime/editor/components/DiffApproval/types.d.ts +32 -0
  60. package/dist/runtime/editor/components/DiffApproval/types.js +22 -0
  61. package/dist/runtime/editor/components/Dropdown/index.d.vue.ts +5 -4
  62. package/dist/runtime/editor/components/Dropdown/index.vue +89 -29
  63. package/dist/runtime/editor/components/Dropdown/index.vue.d.ts +5 -4
  64. package/dist/runtime/editor/components/FlexTextarea/index.d.vue.ts +1 -1
  65. package/dist/runtime/editor/components/FlexTextarea/index.vue.d.ts +1 -1
  66. package/dist/runtime/editor/components/NestedEditorOverlay/index.d.vue.ts +2 -2
  67. package/dist/runtime/editor/components/NestedEditorOverlay/index.vue +3 -3
  68. package/dist/runtime/editor/components/NestedEditorOverlay/index.vue.d.ts +2 -2
  69. package/dist/runtime/editor/components/PopupHost/index.d.vue.ts +13 -0
  70. package/dist/runtime/editor/components/PopupHost/index.vue +12 -0
  71. package/dist/runtime/editor/components/PopupHost/index.vue.d.ts +13 -0
  72. package/dist/runtime/editor/components/index.d.ts +2 -0
  73. package/dist/runtime/editor/components/index.js +2 -0
  74. package/dist/runtime/editor/css/output.css +1 -1
  75. package/dist/runtime/editor/features/analyze/Main.vue +15 -2
  76. package/dist/runtime/editor/features/analyze/Results/ResultsItemNodes.vue +4 -1
  77. package/dist/runtime/editor/features/analyze/Results/ResultsItemNodesTarget.vue +25 -16
  78. package/dist/runtime/editor/features/analyze/analyzers/defaults/validations.d.ts +10 -0
  79. package/dist/runtime/editor/features/analyze/analyzers/defaults/validations.js +39 -0
  80. package/dist/runtime/editor/features/analyze/analyzers/helpers/Context.d.ts +5 -1
  81. package/dist/runtime/editor/features/analyze/analyzers/helpers/Context.js +5 -0
  82. package/dist/runtime/editor/features/analyze/index.vue +0 -1
  83. package/dist/runtime/editor/features/changelog/changelog.json +9 -1
  84. package/dist/runtime/editor/features/comments/Comment/Meta/index.vue +1 -1
  85. package/dist/runtime/editor/features/comments/Comment/index.vue +1 -1
  86. package/dist/runtime/editor/features/publish/Dialog/Violations.d.vue.ts +12 -0
  87. package/dist/runtime/editor/features/publish/Dialog/Violations.vue +117 -0
  88. package/dist/runtime/editor/features/publish/Dialog/Violations.vue.d.ts +12 -0
  89. package/dist/runtime/editor/features/publish/Dialog/index.vue +61 -24
  90. package/dist/runtime/editor/features/publish/index.vue +2 -4
  91. package/dist/runtime/editor/features/publish/types.d.ts +0 -4
  92. package/dist/runtime/editor/helpers/color/index.d.ts +7 -0
  93. package/dist/runtime/editor/helpers/color/index.js +14 -0
  94. package/dist/runtime/editor/helpers/diff/index.d.ts +87 -0
  95. package/dist/runtime/editor/helpers/diff/index.js +256 -0
  96. package/dist/runtime/editor/helpers/injections.d.ts +11 -0
  97. package/dist/runtime/editor/helpers/injections.js +1 -0
  98. package/dist/runtime/editor/plugins/Sidebar/Detached/index.d.vue.ts +1 -1
  99. package/dist/runtime/editor/plugins/Sidebar/Detached/index.vue.d.ts +1 -1
  100. package/dist/runtime/editor/plugins/Sidebar/index.d.vue.ts +2 -2
  101. package/dist/runtime/editor/plugins/Sidebar/index.vue.d.ts +2 -2
  102. package/dist/runtime/editor/providers/analyze.js +5 -2
  103. package/dist/runtime/editor/providers/config.d.ts +3 -1
  104. package/dist/runtime/editor/providers/config.js +46 -15
  105. package/dist/runtime/editor/providers/state.d.ts +13 -0
  106. package/dist/runtime/editor/providers/state.js +7 -0
  107. package/dist/runtime/editor/translations/de.json +7 -4
  108. package/dist/runtime/editor/translations/fr.json +2 -4
  109. package/dist/runtime/editor/translations/gsw_CH.json +7 -4
  110. package/dist/runtime/editor/translations/it.json +2 -4
  111. package/dist/runtime/helpers/colors.d.ts +39 -0
  112. package/dist/runtime/helpers/colors.js +28 -0
  113. package/dist/runtime/types/colors.d.ts +11 -0
  114. package/package.json +1 -1
  115. package/dist/runtime/editor/features/validations/Overlay/Item.d.vue.ts +0 -7
  116. package/dist/runtime/editor/features/validations/Overlay/Item.vue +0 -36
  117. package/dist/runtime/editor/features/validations/Overlay/Item.vue.d.ts +0 -7
  118. package/dist/runtime/editor/features/validations/Overlay/index.d.vue.ts +0 -7
  119. package/dist/runtime/editor/features/validations/Overlay/index.vue +0 -115
  120. package/dist/runtime/editor/features/validations/Overlay/index.vue.d.ts +0 -7
  121. package/dist/runtime/editor/features/validations/SidebarItem/index.d.vue.ts +0 -10
  122. package/dist/runtime/editor/features/validations/SidebarItem/index.vue +0 -41
  123. package/dist/runtime/editor/features/validations/SidebarItem/index.vue.d.ts +0 -10
  124. package/dist/runtime/editor/features/validations/index.d.vue.ts +0 -3
  125. package/dist/runtime/editor/features/validations/index.vue +0 -91
  126. package/dist/runtime/editor/features/validations/index.vue.d.ts +0 -3
  127. package/dist/runtime/editor/types/config.d.ts +0 -5
  128. /package/dist/runtime/{editor/types/config.js → types/colors.js} +0 -0
@@ -1,9 +1,11 @@
1
1
  <template>
2
2
  <Toolbar
3
- v-if="currentItem"
4
- :current-item
5
- :current-index
6
- :total-items="items.length"
3
+ v-if="currentUnit"
4
+ :current-unit
5
+ :current-item="currentUnit.item"
6
+ :unit-index="currentIndex + 1"
7
+ :total-units="units.length"
8
+ :segment-index="segmentIndex"
7
9
  :selected
8
10
  :reasons
9
11
  :apply-label
@@ -20,6 +22,7 @@
20
22
  ref="highlight"
21
23
  v-model="currentIndex"
22
24
  :items
25
+ :units
23
26
  :selected
24
27
  :insertions-only
25
28
  @toggle="onToggle"
@@ -41,6 +44,7 @@ import Toolbar from "./Toolbar/index.vue";
41
44
  import Highlight from "./Highlight/index.vue";
42
45
  import { onBlokkliEvent } from "#blokkli/editor/composables";
43
46
  import { itemEntityType } from "#blokkli-build/config";
47
+ import { unitsFromItems } from "./types";
44
48
  const props = defineProps({
45
49
  items: { type: Array, required: true },
46
50
  showReason: { type: Boolean, required: false },
@@ -76,31 +80,40 @@ const items = [...props.items].sort((a, b) => {
76
80
  if (dy !== 0) return dy;
77
81
  return rectA.x - rectB.x;
78
82
  });
83
+ const units = computed(() => unitsFromItems(items));
79
84
  const currentIndex = ref(0);
80
- const currentItem = computed(() => {
81
- return items.at(currentIndex.value) ?? null;
85
+ const currentUnit = computed(() => {
86
+ return units.value.at(currentIndex.value) ?? null;
87
+ });
88
+ const segmentIndex = computed(() => {
89
+ const unit = currentUnit.value;
90
+ if (!unit || unit.kind !== "segment") return 0;
91
+ const fieldUnits = units.value.filter(
92
+ (u) => u.kind === "segment" && u.item.id === unit.item.id
93
+ );
94
+ return fieldUnits.findIndex((u) => u.key === unit.key) + 1;
82
95
  });
83
96
  const selected = reactive(
84
- Object.fromEntries(items.map((item) => [item.id, true]))
97
+ Object.fromEntries(units.value.map((u) => [u.key, true]))
85
98
  );
86
99
  const reasons = reactive(
87
- Object.fromEntries(items.map((item) => [item.id, ""]))
100
+ Object.fromEntries(units.value.map((u) => [u.key, ""]))
88
101
  );
89
102
  const selectedCount = computed(
90
- () => items.filter((item) => selected[item.id]).length
103
+ () => units.value.filter((u) => selected[u.key]).length
91
104
  );
92
105
  const applyLabel = computed(() => {
93
- return $t("aiAgentBatchRewriteApply", "Apply @count of @total").replace("@count", selectedCount.value.toString()).replace("@total", items.length.toString());
106
+ return $t("aiAgentBatchRewriteApply", "Apply @count of @total").replace("@count", selectedCount.value.toString()).replace("@total", units.value.length.toString());
94
107
  });
95
- function onUpdateSelected(id, value) {
96
- selected[id] = value;
108
+ function onUpdateSelected(key, value) {
109
+ selected[key] = value;
97
110
  nextTick(() => highlight.value?.updateRects());
98
111
  }
99
- function onToggle(id) {
100
- onUpdateSelected(id, !selected[id]);
112
+ function onToggle(key) {
113
+ onUpdateSelected(key, !selected[key]);
101
114
  }
102
- function onUpdateReasons(id, value) {
103
- reasons[id] = value;
115
+ function onUpdateReasons(key, value) {
116
+ reasons[key] = value;
104
117
  }
105
118
  function onApply() {
106
119
  highlight.value?.commitSelected();
@@ -109,24 +122,37 @@ function onApply() {
109
122
  reasons: { ...reasons }
110
123
  });
111
124
  }
112
- function scrollToItem(item) {
113
- const host = resolveHost(item.uuid);
125
+ function scrollToUnit(unit) {
126
+ const host = resolveHost(unit.item.uuid);
114
127
  if (host) {
115
- const el = directive.findEditableElement(item.fieldName, host);
116
- if (el) {
117
- eventBus.emit("scrollIntoView", { element: el, immediate: false });
128
+ const fieldEl = directive.findEditableElement(unit.item.fieldName, host);
129
+ if (fieldEl) {
130
+ let target = fieldEl;
131
+ if (unit.kind === "segment") {
132
+ const segEl = fieldEl.querySelector(
133
+ `[data-chunk-index="${CSS.escape(unit.segment.id)}"]`
134
+ );
135
+ if (segEl) target = segEl;
136
+ }
137
+ eventBus.emit("scrollIntoView", { element: target, immediate: false });
118
138
  return;
119
139
  }
120
140
  }
121
- eventBus.emit("scrollIntoView", { uuid: item.uuid, immediate: false });
141
+ eventBus.emit("scrollIntoView", { uuid: unit.item.uuid, immediate: false });
122
142
  }
123
143
  function prev() {
124
- currentIndex.value = (currentIndex.value - 1 + items.length) % items.length;
125
- scrollToItem(items[currentIndex.value]);
144
+ const total = units.value.length;
145
+ if (total === 0) return;
146
+ currentIndex.value = (currentIndex.value - 1 + total) % total;
147
+ const u = units.value[currentIndex.value];
148
+ if (u) scrollToUnit(u);
126
149
  }
127
150
  function next() {
128
- currentIndex.value = (currentIndex.value + 1) % items.length;
129
- scrollToItem(items[currentIndex.value]);
151
+ const total = units.value.length;
152
+ if (total === 0) return;
153
+ currentIndex.value = (currentIndex.value + 1) % total;
154
+ const u = units.value[currentIndex.value];
155
+ if (u) scrollToUnit(u);
130
156
  }
131
157
  onBlokkliEvent("keyPressed", (e) => {
132
158
  if (e.code === "Tab" && !e.shift || e.code === "ArrowDown") {
@@ -137,25 +163,26 @@ onBlokkliEvent("keyPressed", (e) => {
137
163
  prev();
138
164
  } else if (e.code === " ") {
139
165
  e.originalEvent.preventDefault();
140
- const item = items[currentIndex.value];
141
- if (item) {
142
- onUpdateSelected(item.id, !selected[item.id]);
166
+ const u = units.value[currentIndex.value];
167
+ if (u) {
168
+ onUpdateSelected(u.key, !selected[u.key]);
143
169
  }
144
170
  }
145
171
  });
146
172
  onBlokkliEvent("editable:focus", (e) => {
147
- const index = items.findIndex(
148
- (item) => item.fieldName === e.fieldName && item.uuid === e.uuid
173
+ const idx = units.value.findIndex(
174
+ (u) => u.item.fieldName === e.fieldName && u.item.uuid === e.uuid
149
175
  );
150
- if (index !== -1) {
151
- currentIndex.value = index;
176
+ if (idx !== -1) {
177
+ currentIndex.value = idx;
152
178
  }
153
179
  });
154
180
  onMounted(async () => {
155
181
  ui.setIsApproving(true);
156
182
  await nextTick();
157
- if (items[0]) {
158
- scrollToItem(items[0]);
183
+ const first = units.value[0];
184
+ if (first) {
185
+ scrollToUnit(first);
159
186
  }
160
187
  });
161
188
  onBeforeUnmount(() => {
@@ -2,14 +2,14 @@ import type { ApprovalItem } from './types.js';
2
2
  type __VLS_Props = {
3
3
  items: ApprovalItem[];
4
4
  /**
5
- * Whether to show the per-item rejection reason input.
5
+ * Whether to show the per-unit rejection reason input.
6
6
  *
7
7
  * Used by the agent tools to feed feedback back to the LLM. Leave it off when
8
8
  * changes are applied directly with no agent loop.
9
9
  */
10
10
  showReason?: boolean;
11
11
  /**
12
- * Render the new value entirely as an insertion (<ins>) instead of a diff
12
+ * Render new values entirely as insertions (<ins>) instead of a diff
13
13
  * against the original.
14
14
  *
15
15
  * For features like translation the new text bears little resemblance to the
@@ -21,14 +21,22 @@ type __VLS_Props = {
21
21
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
22
22
  cancel: () => any;
23
23
  apply: (data: {
24
- selected: Record<number, boolean>;
25
- reasons: Record<number, string>;
24
+ /**
25
+ * Acceptance keyed by unit. For unsegmented items the key is the
26
+ * stringified item id; for segmented items it's `${itemId}:${segmentId}`.
27
+ */
28
+ selected: Record<string, boolean>;
29
+ reasons: Record<string, string>;
26
30
  }) => any;
27
31
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
28
32
  onCancel?: (() => any) | undefined;
29
33
  onApply?: ((data: {
30
- selected: Record<number, boolean>;
31
- reasons: Record<number, string>;
34
+ /**
35
+ * Acceptance keyed by unit. For unsegmented items the key is the
36
+ * stringified item id; for segmented items it's `${itemId}:${segmentId}`.
37
+ */
38
+ selected: Record<string, boolean>;
39
+ reasons: Record<string, string>;
32
40
  }) => any) | undefined;
33
41
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
34
42
  declare const _default: typeof __VLS_export;
@@ -1,7 +1,39 @@
1
+ import type { AtomicSegment, Segment } from '#blokkli/editor/helpers/diff';
1
2
  export type ApprovalItem = {
2
3
  id: number;
3
4
  uuid: string;
4
5
  fieldName: string;
5
6
  fieldLabel: string;
7
+ /**
8
+ * The fully-accepted value for the field. When `segments` is present this is
9
+ * still the canonical "all accepted" value (`reassembleValue(segments, {})`),
10
+ * kept for tools that don't care about chunk-level acceptance.
11
+ */
6
12
  value: string;
13
+ /**
14
+ * Two-level segmentation of the rewrite: top-level blocks plus one-level
15
+ * recursion into <ul>/<ol> for per-<li> toggles. When absent the field is
16
+ * treated as a single all-or-nothing unit.
17
+ */
18
+ segments?: Segment[];
7
19
  };
20
+ /**
21
+ * One thing the user can accept or reject from the keyboard or a click.
22
+ * For unsegmented items there's one whole-field unit; for segmented items
23
+ * there's one unit per atomic, *changed* segment.
24
+ */
25
+ export type ApprovalUnit = {
26
+ kind: 'whole';
27
+ key: string;
28
+ item: ApprovalItem;
29
+ } | {
30
+ kind: 'segment';
31
+ key: string;
32
+ item: ApprovalItem;
33
+ segment: AtomicSegment;
34
+ };
35
+ /**
36
+ * Expand items into their toggle units. Unchanged segments inside a list are
37
+ * skipped — they're context, not choices.
38
+ */
39
+ export declare function unitsFromItems(items: ApprovalItem[]): ApprovalUnit[];
@@ -0,0 +1,22 @@
1
+ import { flattenSegments } from "#blokkli/editor/helpers/diff";
2
+ export function unitsFromItems(items) {
3
+ const units = [];
4
+ for (const item of items) {
5
+ if (item.segments) {
6
+ const atoms = flattenSegments(item.segments).filter(
7
+ (s) => s.status !== "matched" || s.beforeHtml !== s.afterHtml
8
+ );
9
+ for (const segment of atoms) {
10
+ units.push({
11
+ kind: "segment",
12
+ key: `${item.id}:${segment.id}`,
13
+ item,
14
+ segment
15
+ });
16
+ }
17
+ } else {
18
+ units.push({ kind: "whole", key: String(item.id), item });
19
+ }
20
+ }
21
+ return units;
22
+ }
@@ -1,22 +1,23 @@
1
+ type DropdownPosition = 'bottom-left' | 'top-left' | 'top-right';
1
2
  type __VLS_Props = {
2
- position?: 'bottom-left' | 'top-left' | 'top-right';
3
+ position?: DropdownPosition;
3
4
  disabled?: boolean;
4
5
  buttonClass?: string;
5
6
  };
6
7
  declare function close(): void;
7
- declare var __VLS_1: {}, __VLS_9: {
8
+ declare var __VLS_1: {}, __VLS_15: {
8
9
  close: typeof close;
9
10
  };
10
11
  type __VLS_Slots = {} & {
11
12
  button?: (props: typeof __VLS_1) => any;
12
13
  } & {
13
- default?: (props: typeof __VLS_9) => any;
14
+ default?: (props: typeof __VLS_15) => any;
14
15
  };
15
16
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
16
17
  close: typeof close;
17
18
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
18
19
  disabled: boolean;
19
- position: "bottom-left" | "top-left" | "top-right";
20
+ position: DropdownPosition;
20
21
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
22
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
22
23
  declare const _default: typeof __VLS_export;
@@ -8,17 +8,20 @@
8
8
  >
9
9
  <slot name="button" />
10
10
  </button>
11
- <BlokkliTransition name="drop-up">
12
- <div
13
- v-if="showMenu"
14
- ref="contentEl"
15
- class="bk-dropdown-menu-content _bk_rounded"
16
- :class="positionClass"
17
- @keydown="onContentKeydown"
18
- >
19
- <slot :close="close" />
20
- </div>
21
- </BlokkliTransition>
11
+ <Teleport :to="teleport ?? 'body'" :disabled="!teleport">
12
+ <BlokkliTransition name="drop-up">
13
+ <div
14
+ v-if="showMenu"
15
+ ref="contentEl"
16
+ class="bk-dropdown-menu-content _bk_rounded"
17
+ :class="[originClass, !teleport ? positionClass : null]"
18
+ :style="teleport ? floatingStyle : void 0"
19
+ @keydown="onContentKeydown"
20
+ >
21
+ <slot :close="close" />
22
+ </div>
23
+ </BlokkliTransition>
24
+ </Teleport>
22
25
  </div>
23
26
  </template>
24
27
 
@@ -30,19 +33,65 @@ import {
30
33
  nextTick,
31
34
  onMounted,
32
35
  onBeforeUnmount,
33
- useTemplateRef
36
+ useTemplateRef,
37
+ inject
34
38
  } from "#imports";
35
39
  import { BlokkliTransition } from "#blokkli/editor/components";
36
40
  import { onBlokkliEvent } from "#blokkli/editor/composables";
41
+ import {
42
+ autoUpdate,
43
+ computePosition,
44
+ flip,
45
+ offset,
46
+ shift
47
+ } from "@floating-ui/dom";
48
+ import { INJECT_POPUP_HOST } from "#blokkli/editor/helpers/injections";
37
49
  const props = defineProps({
38
50
  position: { type: String, required: false, default: "bottom-left" },
39
51
  disabled: { type: Boolean, required: false, default: false },
40
52
  buttonClass: { type: String, required: false }
41
53
  });
54
+ const popupHostRef = inject(INJECT_POPUP_HOST, null);
55
+ const teleport = computed(() => popupHostRef?.value ?? null);
42
56
  const container = useTemplateRef("container");
43
57
  const contentEl = useTemplateRef("contentEl");
44
58
  const showMenu = ref(false);
45
59
  const positionClass = computed(() => `bk-is-${props.position}`);
60
+ const originClass = computed(() => `bk-origin-${props.position}`);
61
+ const PLACEMENT_MAP = {
62
+ "bottom-left": "top-start",
63
+ "top-left": "bottom-start",
64
+ "top-right": "bottom-end"
65
+ };
66
+ const floatingX = ref(0);
67
+ const floatingY = ref(0);
68
+ const floatingStyle = computed(() => ({
69
+ position: "fixed",
70
+ top: `${floatingY.value}px`,
71
+ left: `${floatingX.value}px`
72
+ }));
73
+ let stopAutoUpdate = null;
74
+ function getTriggerEl() {
75
+ return container.value?.querySelector(".bk-dropdown-menu-trigger") ?? null;
76
+ }
77
+ async function updateFloatingPosition() {
78
+ const triggerEl = getTriggerEl();
79
+ const floatingEl = contentEl.value;
80
+ if (!triggerEl || !floatingEl) return;
81
+ const { x, y } = await computePosition(triggerEl, floatingEl, {
82
+ placement: PLACEMENT_MAP[props.position],
83
+ strategy: "fixed",
84
+ middleware: [offset(5), flip(), shift({ padding: 8 })]
85
+ });
86
+ floatingX.value = x;
87
+ floatingY.value = y;
88
+ }
89
+ function stopFloating() {
90
+ if (stopAutoUpdate) {
91
+ stopAutoUpdate();
92
+ stopAutoUpdate = null;
93
+ }
94
+ }
46
95
  function getFocusableItems() {
47
96
  if (!contentEl.value) return [];
48
97
  return Array.from(
@@ -50,10 +99,7 @@ function getFocusableItems() {
50
99
  );
51
100
  }
52
101
  function focusTrigger() {
53
- const trigger = container.value?.querySelector(
54
- ".bk-dropdown-menu-trigger"
55
- );
56
- trigger?.focus();
102
+ getTriggerEl()?.focus();
57
103
  }
58
104
  function close() {
59
105
  if (!showMenu.value) return;
@@ -63,9 +109,22 @@ function close() {
63
109
  watch(showMenu, (open) => {
64
110
  if (open) {
65
111
  nextTick(() => {
112
+ if (teleport.value) {
113
+ const triggerEl = getTriggerEl();
114
+ const floatingEl = contentEl.value;
115
+ if (triggerEl && floatingEl) {
116
+ stopAutoUpdate = autoUpdate(
117
+ triggerEl,
118
+ floatingEl,
119
+ updateFloatingPosition
120
+ );
121
+ }
122
+ }
66
123
  const items = getFocusableItems();
67
124
  items[0]?.focus();
68
125
  });
126
+ } else {
127
+ stopFloating();
69
128
  }
70
129
  });
71
130
  function onContentKeydown(e) {
@@ -94,9 +153,10 @@ function onContentKeydown(e) {
94
153
  e.stopPropagation();
95
154
  }
96
155
  function onDocumentClick(e) {
97
- if (!container.value?.contains(e.target)) {
98
- close();
99
- }
156
+ const target = e.target;
157
+ if (container.value?.contains(target)) return;
158
+ if (contentEl.value?.contains(target)) return;
159
+ close();
100
160
  }
101
161
  onBlokkliEvent("mouse:up", close);
102
162
  onMounted(() => {
@@ -104,31 +164,25 @@ onMounted(() => {
104
164
  });
105
165
  onBeforeUnmount(() => {
106
166
  document.removeEventListener("click", onDocumentClick);
167
+ stopFloating();
107
168
  });
108
169
  defineExpose({ close });
109
170
  </script>
110
171
 
111
172
  <style>/*! tailwindcss v4.2.4 | MIT License | https://tailwindcss.com */
112
173
  .bk .bk-dropdown-menu-trigger {
113
- margin: 0px;
114
- display: block;
115
- cursor: pointer;
116
174
  appearance: none;
117
- --bk-tw-border-style: none;
118
- border-style: none;
119
- background-color: transparent;
120
- padding: 0px;
121
175
  }
122
176
  .bk .bk-dropdown-menu-content {
123
177
  position: absolute;
124
178
  z-index: 50;
125
- overflow: hidden;
126
179
  border-style: var(--bk-tw-border-style);
127
180
  border-width: 1px;
128
181
  border-color: rgb(var(--bk-theme-mono-300) / 1);
129
182
  background-color: white;
130
183
  --bk-tw-shadow: 0 10px 15px -3px var(--bk-tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--bk-tw-shadow-color, rgb(0 0 0 / 0.1));
131
184
  box-shadow: var(--bk-tw-inset-shadow), var(--bk-tw-inset-ring-shadow), var(--bk-tw-ring-offset-shadow), var(--bk-tw-ring-shadow), var(--bk-tw-shadow);
185
+ pointer-events: auto;
132
186
  }
133
187
  :is(.bk .bk-dropdown-menu-content) hr {
134
188
  border-top-color: rgb(var(--bk-theme-mono-300) / 1);
@@ -137,18 +191,24 @@ defineExpose({ close });
137
191
  bottom: 100%;
138
192
  left: 0px;
139
193
  margin-bottom: 5px;
140
- transform-origin: 0 100%;
141
194
  }
142
195
  .bk-is-top-left:is(.bk .bk-dropdown-menu-content) {
143
196
  top: 100%;
144
197
  left: 0px;
145
198
  margin-top: 5px;
146
- transform-origin: 0 0;
147
199
  }
148
200
  .bk-is-top-right:is(.bk .bk-dropdown-menu-content) {
149
201
  top: 100%;
150
202
  right: 0px;
151
203
  margin-top: 5px;
204
+ }
205
+ .bk-origin-bottom-left:is(.bk .bk-dropdown-menu-content) {
206
+ transform-origin: 0 100%;
207
+ }
208
+ .bk-origin-top-left:is(.bk .bk-dropdown-menu-content) {
209
+ transform-origin: 0 0;
210
+ }
211
+ .bk-origin-top-right:is(.bk .bk-dropdown-menu-content) {
152
212
  transform-origin: 100% 0;
153
213
  }
154
214
  </style>
@@ -1,22 +1,23 @@
1
+ type DropdownPosition = 'bottom-left' | 'top-left' | 'top-right';
1
2
  type __VLS_Props = {
2
- position?: 'bottom-left' | 'top-left' | 'top-right';
3
+ position?: DropdownPosition;
3
4
  disabled?: boolean;
4
5
  buttonClass?: string;
5
6
  };
6
7
  declare function close(): void;
7
- declare var __VLS_1: {}, __VLS_9: {
8
+ declare var __VLS_1: {}, __VLS_15: {
8
9
  close: typeof close;
9
10
  };
10
11
  type __VLS_Slots = {} & {
11
12
  button?: (props: typeof __VLS_1) => any;
12
13
  } & {
13
- default?: (props: typeof __VLS_9) => any;
14
+ default?: (props: typeof __VLS_15) => any;
14
15
  };
15
16
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
16
17
  close: typeof close;
17
18
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
18
19
  disabled: boolean;
19
- position: "bottom-left" | "top-left" | "top-right";
20
+ position: DropdownPosition;
20
21
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
22
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
22
23
  declare const _default: typeof __VLS_export;
@@ -28,8 +28,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
28
28
  onSubmit?: (() => any) | undefined;
29
29
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
30
30
  }>, {
31
- maxHeight: number;
32
31
  minHeight: number;
32
+ maxHeight: number;
33
33
  onBeforePaste: (data: ClipboardData) => boolean;
34
34
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
35
35
  declare const _default: typeof __VLS_export;
@@ -28,8 +28,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
28
28
  onSubmit?: (() => any) | undefined;
29
29
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
30
30
  }>, {
31
- maxHeight: number;
32
31
  minHeight: number;
32
+ maxHeight: number;
33
33
  onBeforePaste: (data: ClipboardData) => boolean;
34
34
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
35
35
  declare const _default: typeof __VLS_export;
@@ -10,9 +10,9 @@ export type NestedEditorOverlayProps = {
10
10
  element?: HTMLElement | null;
11
11
  autoSave?: boolean;
12
12
  };
13
- declare var __VLS_38: {};
13
+ declare var __VLS_44: {};
14
14
  type __VLS_Slots = {} & {
15
- default?: (props: typeof __VLS_38) => any;
15
+ default?: (props: typeof __VLS_44) => any;
16
16
  };
17
17
  declare const __VLS_base: import("vue").DefineComponent<NestedEditorOverlayProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
18
18
  close: (...args: any[]) => void;
@@ -35,7 +35,7 @@
35
35
  @after-leave="onAfterLeave"
36
36
  @leave-cancelled="onAfterLeave"
37
37
  >
38
- <div
38
+ <PopupHost
39
39
  v-show="isLoaded"
40
40
  class="bk bk-library-edit-overlay _bk_fixed _bk_top-40 _bk_3xl:top-50 _bk_left-0 _bk_w-screen _bk_bottom-0 _bk_flex _bk_flex-col _bk_pointer-events-auto _bk_z-nested-editor-overlay-iframe"
41
41
  :class="'bk-is-' + theme"
@@ -56,7 +56,7 @@
56
56
  </div>
57
57
  </slot>
58
58
  </div>
59
- </div>
59
+ </PopupHost>
60
60
  </Transition>
61
61
  </Teleport>
62
62
  </template>
@@ -71,7 +71,7 @@ import {
71
71
  useBlokkli,
72
72
  useTemplateRef
73
73
  } from "#imports";
74
- import { Icon, NotEditStateInfo } from "#blokkli/editor/components";
74
+ import { Icon, NotEditStateInfo, PopupHost } from "#blokkli/editor/components";
75
75
  import { onBroadcastEvent } from "#blokkli/editor/composables";
76
76
  const props = defineProps({
77
77
  url: { type: String, required: false },
@@ -10,9 +10,9 @@ export type NestedEditorOverlayProps = {
10
10
  element?: HTMLElement | null;
11
11
  autoSave?: boolean;
12
12
  };
13
- declare var __VLS_38: {};
13
+ declare var __VLS_44: {};
14
14
  type __VLS_Slots = {} & {
15
- default?: (props: typeof __VLS_38) => any;
15
+ default?: (props: typeof __VLS_44) => any;
16
16
  };
17
17
  declare const __VLS_base: import("vue").DefineComponent<NestedEditorOverlayProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
18
18
  close: (...args: any[]) => void;
@@ -0,0 +1,13 @@
1
+ declare var __VLS_1: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_1) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <div ref="root">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import { provide, useTemplateRef } from "#imports";
9
+ import { INJECT_POPUP_HOST } from "#blokkli/editor/helpers/injections";
10
+ const root = useTemplateRef("root");
11
+ provide(INJECT_POPUP_HOST, root);
12
+ </script>
@@ -0,0 +1,13 @@
1
+ declare var __VLS_1: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_1) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };