@blokkli/editor 2.0.0-alpha.47 → 2.0.0-alpha.49

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 (42) hide show
  1. package/dist/chunks/tailwindConfig.mjs +222 -0
  2. package/dist/global/types/definitions.d.ts +1 -1
  3. package/dist/module.d.mts +2 -2
  4. package/dist/module.json +1 -1
  5. package/dist/module.mjs +164 -16
  6. package/dist/modules/agent/index.d.mts +1 -1
  7. package/dist/modules/agent/index.mjs +6 -29
  8. package/dist/modules/charts/index.d.mts +1 -1
  9. package/dist/modules/charts/index.mjs +3 -3
  10. package/dist/modules/charts/runtime/features/charts/Editor/index.d.vue.ts +1 -0
  11. package/dist/modules/charts/runtime/features/charts/Editor/index.vue +50 -53
  12. package/dist/modules/charts/runtime/features/charts/Editor/index.vue.d.ts +1 -0
  13. package/dist/modules/drupal/index.d.mts +1 -1
  14. package/dist/modules/drupal/index.mjs +1 -1
  15. package/dist/modules/index.d.mts +7 -0
  16. package/dist/{shared/editor.CGf7C_0M.mjs → modules/index.mjs} +1 -1
  17. package/dist/modules/table-of-contents/index.d.mts +1 -1
  18. package/dist/modules/table-of-contents/index.mjs +1 -1
  19. package/dist/modules/tailwind/index.d.mts +5 -0
  20. package/dist/modules/tailwind/index.mjs +2 -0
  21. package/dist/runtime/editor/components/NestedEditorOverlay/index.vue +4 -4
  22. package/dist/runtime/editor/css/output.css +1 -1
  23. package/dist/runtime/editor/features/add-list/index.vue +1 -1
  24. package/dist/runtime/editor/features/anchors/Overlay/index.vue +25 -7
  25. package/dist/runtime/editor/features/changelog/changelog.json +9 -1
  26. package/dist/runtime/editor/features/complex-options/index.vue +98 -0
  27. package/dist/runtime/editor/features/dragging-overlay/index.vue +29 -13
  28. package/dist/runtime/editor/features/media-library/Library/FilterSelect/index.d.vue.ts +15 -0
  29. package/dist/runtime/editor/features/media-library/Library/FilterSelect/index.vue +168 -0
  30. package/dist/runtime/editor/features/media-library/Library/FilterSelect/index.vue.d.ts +15 -0
  31. package/dist/runtime/editor/features/media-library/Library/index.vue +21 -16
  32. package/dist/runtime/editor/providers/keyboard.js +8 -5
  33. package/dist/runtime/editor/translations/de.json +22 -14
  34. package/dist/runtime/editor/translations/fr.json +21 -13
  35. package/dist/runtime/editor/translations/gsw_CH.json +459 -451
  36. package/dist/runtime/editor/translations/it.json +21 -13
  37. package/dist/shared/{editor.BVregnEC.d.mts → editor.DsGJIlGn.d.mts} +19 -2
  38. package/dist/types.d.mts +1 -1
  39. package/package.json +36 -1
  40. package/dist/modules/charts/runtime/features/charts/index.vue +0 -72
  41. /package/dist/{modules/charts/runtime/features/charts → runtime/editor/features/complex-options}/index.d.vue.ts +0 -0
  42. /package/dist/{modules/charts/runtime/features/charts → runtime/editor/features/complex-options}/index.vue.d.ts +0 -0
@@ -118,7 +118,7 @@ defineDropHandler("new", {
118
118
  field.hostEntityBundle
119
119
  );
120
120
  const addBehaviour = definition?.editor?.addBehaviour || "form";
121
- if (definition?.editor?.disableEdit || addBehaviour === "no-form" || addBehaviour.startsWith("editable:") || !adapter.formFrameBuilder) {
121
+ if (definition?.editor?.disableEdit || addBehaviour === "no-form" || addBehaviour.startsWith("editable:") || addBehaviour.startsWith("complex-option:") || !adapter.formFrameBuilder) {
122
122
  await state.mutateWithLoadingState(
123
123
  () => adapter.addNewBlock({
124
124
  bundle: itemBundle,
@@ -11,16 +11,27 @@
11
11
  </template>
12
12
 
13
13
  <script setup>
14
- import { useBlokkli, useRoute, computed } from "#imports";
14
+ import { useBlokkli, useRoute, ref, onMounted } from "#imports";
15
15
  import { PluginBlockIndicator } from "#blokkli/editor/plugins";
16
16
  import { emitMessage } from "#blokkli/editor/events";
17
+ import { onBlokkliEvent } from "#blokkli/editor/composables";
17
18
  const route = useRoute();
18
- const { $t, adapter, dom } = useBlokkli();
19
- const items = computed(() => {
19
+ const { $t, adapter, ui } = useBlokkli();
20
+ function getAnchorItems() {
20
21
  const anchorItems = [];
21
- for (const entry of Object.entries(dom.registeredBlocks.value)) {
22
- const uuid = entry[0];
23
- const element = entry[1];
22
+ const nodes = [...ui.providerElement.querySelectorAll("[id]")];
23
+ for (const element of nodes) {
24
+ if (!(element instanceof HTMLElement)) {
25
+ continue;
26
+ }
27
+ const block = element.closest("[data-bk-uuid]");
28
+ if (!(block instanceof HTMLElement)) {
29
+ continue;
30
+ }
31
+ const uuid = block.dataset.bkUuid;
32
+ if (!uuid) {
33
+ continue;
34
+ }
24
35
  if (!element || !uuid) {
25
36
  continue;
26
37
  }
@@ -32,7 +43,8 @@ const items = computed(() => {
32
43
  }
33
44
  }
34
45
  return anchorItems;
35
- });
46
+ }
47
+ const items = ref([]);
36
48
  function getLinkForClipboard(item) {
37
49
  if (adapter.buildAnchorLink) {
38
50
  return adapter.buildAnchorLink(item.id, item.uuid);
@@ -50,4 +62,10 @@ function onClick(item) {
50
62
  emitMessage(message, "success", void 0, true);
51
63
  }
52
64
  }
65
+ onBlokkliEvent("state:reloaded", () => {
66
+ items.value = getAnchorItems();
67
+ });
68
+ onMounted(() => {
69
+ items.value = getAnchorItems();
70
+ });
53
71
  </script>
@@ -1,9 +1,17 @@
1
1
  [
2
+ {
3
+ "version": "2.0.0-alpha.48",
4
+ "date": "2026-03-20",
5
+ "body": {
6
+ "en": "<h3>Improvements</h3>\n<ul>\n<li>The media library filters now use custom dropdown selects with keyboard\nnavigation and a search field for long lists, replacing the native browser\nselects.</li>\n</ul>\n",
7
+ "de": "<h3>Verbesserungen</h3>\n<ul>\n<li>Die Filter in der Medienbibliothek verwenden jetzt eigene Dropdown-Auswahlen\nmit Tastaturnavigation und einem Suchfeld bei langen Listen anstelle der\nnativen Browser-Auswahlen.</li>\n</ul>\n"
8
+ }
9
+ },
2
10
  {
3
11
  "version": "2.0.0-alpha.47",
4
12
  "date": "2026-03-18",
5
13
  "body": {
6
- "en": "<h3>New Features</h3>\n<h4>Block permissions</h4>\n<p>Administrators can now restrict which block types individual users are allowed to\ncreate, edit, or delete. Restricted blocks are visually marked in the editor.</p>\n<h4>Fragments in block selector</h4>\n<p>When adding a block before or after a selected block, available fragments are now\nlisted directly in the selector by name. Clicking a fragment adds it immediately\nwithout opening a separate dialog.</p>\n<h3>Improvements</h3>\n<ul>\n<li>The block selector now groups items into separate sections for blocks,\nfragments, and actions, making it easier to find the right entry.</li>\n<li>The block selector adjusts its height to the available viewport space, reducing\nunnecessary scrolling when it opens near the top of the screen.</li>\n</ul>\n<h3>Fixes</h3>\n<ul>\n<li>Fixed the analyze tooltip not being positioned and styled correctly.</li>\n<li>Fixed the tooltip transition playing with the wrong origin when the tooltip\nopens above the anchor.</li>\n<li>Fixed the add button tooltip staying visible while the block selector is open.</li>\n</ul>\n",
14
+ "en": "<h3>New Features</h3>\n<h4>Block permissions</h4>\n<p>Administrators can now restrict which block types individual users are allowed\nto create, edit, or delete. Restricted blocks are visually marked in the editor.</p>\n<h4>Fragments in block selector</h4>\n<p>When adding a block before or after a selected block, available fragments are\nnow listed directly in the selector by name. Clicking a fragment adds it\nimmediately without opening a separate dialog.</p>\n<h3>Improvements</h3>\n<ul>\n<li>The block selector now groups items into separate sections for blocks,\nfragments, and actions, making it easier to find the right entry.</li>\n<li>The block selector adjusts its height to the available viewport space,\nreducing unnecessary scrolling when it opens near the top of the screen.</li>\n</ul>\n<h3>Fixes</h3>\n<ul>\n<li>Fixed the analyze tooltip not being positioned and styled correctly.</li>\n<li>Fixed the tooltip transition playing with the wrong origin when the tooltip\nopens above the anchor.</li>\n<li>Fixed the add button tooltip staying visible while the block selector is open.</li>\n</ul>\n",
7
15
  "de": "<h3>Neue Funktionen</h3>\n<h4>Block-Berechtigungen</h4>\n<p>Administratoren können jetzt festlegen, welche Blocktypen einzelne Benutzer\nerstellen, bearbeiten oder löschen dürfen. Eingeschränkte Blöcke werden im\nEditor visuell markiert.</p>\n<h4>Fragmente in der Block-Auswahl</h4>\n<p>Beim Hinzufügen eines Blocks vor oder nach einem ausgewählten Block werden\nverfügbare Fragmente jetzt direkt namentlich in der Auswahl angezeigt. Ein Klick\nauf ein Fragment fügt es sofort hinzu, ohne einen separaten Dialog zu öffnen.</p>\n<h3>Verbesserungen</h3>\n<ul>\n<li>Die Block-Auswahl zeigt Einträge jetzt in getrennten Bereichen für Blöcke,\nFragmente und Aktionen an, was die Übersicht verbessert.</li>\n<li>Die Block-Auswahl passt ihre Höhe an den verfügbaren Platz im Fenster an, was\nunnötiges Scrollen beim Öffnen im oberen Bildschirmbereich reduziert.</li>\n</ul>\n<h3>Fehlerbehebungen</h3>\n<ul>\n<li>Der Analyse-Tooltip wurde nicht korrekt positioniert und dargestellt.</li>\n<li>Die Tooltip-Animation hatte den falschen Ursprung, wenn der Tooltip oberhalb\ndes Ankers geöffnet wurde.</li>\n<li>Der Tooltip der Hinzufügen-Schaltfläche blieb sichtbar, während die\nBlock-Auswahl geöffnet war.</li>\n</ul>\n"
8
16
  }
9
17
  },
@@ -0,0 +1,98 @@
1
+ <template>
2
+ <NestedEditorOverlay
3
+ v-if="uuid"
4
+ :uuid
5
+ :title="config.editorTitle"
6
+ :icon="config.editorIcon"
7
+ theme="accent"
8
+ :element
9
+ @submit="onSubmit"
10
+ @close="onSubmit"
11
+ >
12
+ <component
13
+ :is="config.editorComponent"
14
+ ref="editorRef"
15
+ :data
16
+ :uuid
17
+ :option-key="optionKey"
18
+ />
19
+ </NestedEditorOverlay>
20
+ </template>
21
+
22
+ <script setup>
23
+ import { onBlokkliEvent } from "#blokkli/editor/composables";
24
+ import {
25
+ defineBlokkliFeature,
26
+ ref,
27
+ useTemplateRef,
28
+ useBlokkli,
29
+ computed
30
+ } from "#imports";
31
+ import { NestedEditorOverlay } from "#blokkli/editor/components";
32
+ import { COMPLEX_OPTION_TYPES } from "#blokkli-build/complex-option-types";
33
+ defineBlokkliFeature({
34
+ id: "complex-options",
35
+ icon: "bk_mdi_edit",
36
+ label: "Complex Options",
37
+ description: "Edit complex option types such as charts.",
38
+ requiredAdapterMethods: ["updateOptions"]
39
+ });
40
+ const { $t, state, adapter, dom, blocks } = useBlokkli();
41
+ const uuid = ref(null);
42
+ const optionKey = ref("");
43
+ const dataType = ref("");
44
+ const config = computed(() => {
45
+ if (!dataType.value) {
46
+ return null;
47
+ }
48
+ return COMPLEX_OPTION_TYPES[dataType.value];
49
+ });
50
+ const element = computed(() => {
51
+ if (!uuid.value) {
52
+ return null;
53
+ }
54
+ const block = blocks.getBlock(uuid.value);
55
+ if (!block) {
56
+ return null;
57
+ }
58
+ return dom.getDragElement(block);
59
+ });
60
+ const data = computed(() => {
61
+ if (!uuid.value || !optionKey.value) {
62
+ return null;
63
+ }
64
+ const rawData = state.mutatedOptions[uuid.value]?.[optionKey.value] || state.getFieldListItem(uuid.value)?.options?.[optionKey.value];
65
+ if (rawData) {
66
+ try {
67
+ return JSON.parse(rawData);
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+ return null;
73
+ });
74
+ const editorRef = useTemplateRef("editorRef");
75
+ async function onSubmit() {
76
+ if (!uuid.value || !editorRef.value) return;
77
+ const editorData = editorRef.value.getData();
78
+ await state.mutateWithLoadingState(
79
+ () => adapter.updateOptions([
80
+ {
81
+ uuid: uuid.value,
82
+ key: optionKey.value,
83
+ value: JSON.stringify(editorData)
84
+ }
85
+ ]),
86
+ $t("complexOptionsSaveError", "The data could not be saved.")
87
+ );
88
+ uuid.value = null;
89
+ }
90
+ onBlokkliEvent("option:edit-complex", (e) => {
91
+ if (!(e.dataType in COMPLEX_OPTION_TYPES)) {
92
+ return;
93
+ }
94
+ uuid.value = e.uuid;
95
+ optionKey.value = e.key;
96
+ dataType.value = e.dataType;
97
+ });
98
+ </script>
@@ -228,28 +228,44 @@ onBlokkliEvent("state:reloaded", async function() {
228
228
  }
229
229
  const allSelected = [...selection.uuids.value, newBlock.uuid];
230
230
  eventBus.emit("select", allSelected);
231
- if (!dropResult?.focusEditable) {
232
- return;
233
- }
234
231
  const definition = definitions.getBlockDefinition(
235
232
  newBlock.bundle,
236
233
  newBlock.fieldListType
237
234
  );
238
- if (!definition?.editor?.addBehaviour?.startsWith("editable:")) {
235
+ const addBehaviour = definition?.editor?.addBehaviour;
236
+ if (addBehaviour?.startsWith("complex-option:")) {
237
+ const optionKey = addBehaviour.split(":")[1];
238
+ if (!optionKey) {
239
+ return;
240
+ }
241
+ const option = definition?.options?.[optionKey];
242
+ if (option?.type !== "json" || !option.dataType) {
243
+ return;
244
+ }
245
+ eventBus.emit("option:edit-complex", {
246
+ uuid: newUuid,
247
+ key: optionKey,
248
+ dataType: option.dataType
249
+ });
239
250
  return;
240
251
  }
241
- const editableField = definition.editor.addBehaviour.split(":")[1];
242
- if (!editableField) {
252
+ if (!dropResult?.focusEditable) {
243
253
  return;
244
254
  }
245
- const editableFieldElement = directive.getEditablesForBlock(newUuid).find((v) => v.fieldName === editableField);
246
- if (!editableFieldElement) {
247
- return;
255
+ if (addBehaviour?.startsWith("editable:")) {
256
+ const editableField = addBehaviour.split(":")[1];
257
+ if (!editableField) {
258
+ return;
259
+ }
260
+ const editableFieldElement = directive.getEditablesForBlock(newUuid).find((v) => v.fieldName === editableField);
261
+ if (!editableFieldElement) {
262
+ return;
263
+ }
264
+ eventBus.emit("editable:open", {
265
+ fieldName: editableField,
266
+ uuid: newUuid
267
+ });
248
268
  }
249
- eventBus.emit("editable:open", {
250
- fieldName: editableField,
251
- uuid: newUuid
252
- });
253
269
  });
254
270
  onBlokkliEvent("dragging:move", (e) => {
255
271
  mouseX.value = e.x;
@@ -0,0 +1,15 @@
1
+ type __VLS_Props = {
2
+ label: string;
3
+ options: {
4
+ value: string;
5
+ label: string;
6
+ }[];
7
+ modelValue?: string;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:modelValue": (value: string) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ "onUpdate:modelValue"?: ((value: string) => any) | undefined;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
@@ -0,0 +1,168 @@
1
+ <template>
2
+ <div
3
+ ref="root"
4
+ class="bk-media-library-filter-select"
5
+ :class="{ 'bk-is-open': isOpen }"
6
+ @keydown.stop="onKeydown"
7
+ >
8
+ <button type="button" @click="toggle">
9
+ <div>
10
+ <span class="bk-media-library-filter-select-label">{{ label }}</span>
11
+ <span
12
+ v-if="selectedLabel"
13
+ class="bk-media-library-filter-select-value"
14
+ >{{ selectedLabel }}</span
15
+ >
16
+ </div>
17
+ <Icon name="bk_mdi_arrow_drop_down" />
18
+ </button>
19
+ <div
20
+ v-if="isOpen"
21
+ class="bk-media-library-filter-select-dropdown bk-scrollbar-dark"
22
+ >
23
+ <div
24
+ v-if="options.length > 10"
25
+ class="bk-media-library-filter-select-search"
26
+ >
27
+ <input
28
+ ref="searchInput"
29
+ v-model="search"
30
+ class="bk-form-input bk-is-small"
31
+ type="text"
32
+ :placeholder="$t('filterSelectSearch', 'Search...')"
33
+ />
34
+ </div>
35
+ <ul ref="listEl">
36
+ <li v-for="(option, index) in filteredOptions" :key="option.value">
37
+ <button
38
+ type="button"
39
+ :class="{
40
+ 'bk-is-active': option.value === modelValue,
41
+ 'bk-is-highlighted': index === highlightedIndex
42
+ }"
43
+ @click="select(option.value)"
44
+ @mouseenter="highlightedIndex = index"
45
+ >
46
+ {{ option.label }}
47
+ </button>
48
+ </li>
49
+ <li
50
+ v-if="!filteredOptions.length"
51
+ class="bk-media-library-filter-select-empty"
52
+ >
53
+ {{ $t("filterSelectNoResults", "No results") }}
54
+ </li>
55
+ </ul>
56
+ </div>
57
+ </div>
58
+ </template>
59
+
60
+ <script setup>
61
+ import {
62
+ ref,
63
+ computed,
64
+ nextTick,
65
+ watch,
66
+ useTemplateRef,
67
+ onBeforeUnmount,
68
+ useBlokkli
69
+ } from "#imports";
70
+ import { Icon } from "#blokkli/editor/components";
71
+ const { $t } = useBlokkli();
72
+ const props = defineProps({
73
+ label: { type: String, required: true },
74
+ options: { type: Array, required: true },
75
+ modelValue: { type: String, required: false }
76
+ });
77
+ const emit = defineEmits(["update:modelValue"]);
78
+ const isOpen = ref(false);
79
+ const search = ref("");
80
+ const highlightedIndex = ref(-1);
81
+ const searchInput = useTemplateRef("searchInput");
82
+ const root = useTemplateRef("root");
83
+ const listEl = useTemplateRef("listEl");
84
+ const selectedLabel = computed(() => {
85
+ const option = props.options.find((o) => o.value === props.modelValue);
86
+ return option?.label ?? "";
87
+ });
88
+ const filteredOptions = computed(() => {
89
+ if (!search.value) {
90
+ return props.options;
91
+ }
92
+ const term = search.value.toLowerCase();
93
+ return props.options.filter((o) => o.label.toLowerCase().includes(term));
94
+ });
95
+ watch(filteredOptions, () => {
96
+ highlightedIndex.value = -1;
97
+ });
98
+ function scrollToHighlighted() {
99
+ if (!listEl.value || highlightedIndex.value < 0) {
100
+ return;
101
+ }
102
+ const buttons = listEl.value.querySelectorAll("button");
103
+ buttons[highlightedIndex.value]?.scrollIntoView({ block: "nearest" });
104
+ }
105
+ function toggle() {
106
+ isOpen.value = !isOpen.value;
107
+ if (isOpen.value) {
108
+ search.value = "";
109
+ highlightedIndex.value = -1;
110
+ nextTick(() => {
111
+ searchInput.value?.focus();
112
+ });
113
+ }
114
+ }
115
+ function select(value) {
116
+ emit("update:modelValue", value);
117
+ isOpen.value = false;
118
+ }
119
+ function onKeydown(e) {
120
+ if (!isOpen.value) {
121
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
122
+ e.preventDefault();
123
+ toggle();
124
+ }
125
+ return;
126
+ }
127
+ const options = filteredOptions.value;
128
+ switch (e.key) {
129
+ case "ArrowDown":
130
+ e.preventDefault();
131
+ highlightedIndex.value = highlightedIndex.value < options.length - 1 ? highlightedIndex.value + 1 : 0;
132
+ nextTick(scrollToHighlighted);
133
+ break;
134
+ case "ArrowUp":
135
+ e.preventDefault();
136
+ highlightedIndex.value = highlightedIndex.value > 0 ? highlightedIndex.value - 1 : options.length - 1;
137
+ nextTick(scrollToHighlighted);
138
+ break;
139
+ case "Enter": {
140
+ e.preventDefault();
141
+ const option = options[highlightedIndex.value];
142
+ if (option) {
143
+ select(option.value);
144
+ }
145
+ break;
146
+ }
147
+ case "Escape":
148
+ e.preventDefault();
149
+ isOpen.value = false;
150
+ break;
151
+ }
152
+ }
153
+ function onClickOutside(e) {
154
+ if (root.value && !root.value.contains(e.target)) {
155
+ isOpen.value = false;
156
+ }
157
+ }
158
+ watch(isOpen, (open) => {
159
+ if (open) {
160
+ document.addEventListener("click", onClickOutside, true);
161
+ } else {
162
+ document.removeEventListener("click", onClickOutside, true);
163
+ }
164
+ });
165
+ onBeforeUnmount(() => {
166
+ document.removeEventListener("click", onClickOutside, true);
167
+ });
168
+ </script>
@@ -0,0 +1,15 @@
1
+ type __VLS_Props = {
2
+ label: string;
3
+ options: {
4
+ value: string;
5
+ label: string;
6
+ }[];
7
+ modelValue?: string;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:modelValue": (value: string) => any;
11
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ "onUpdate:modelValue"?: ((value: string) => any) | undefined;
13
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="bk bk-media-library">
2
+ <div class="bk bk-media-library bk-scrollbar-light">
3
3
  <div v-if="status === 'pending'" class="bk-loading">
4
4
  <Icon name="loader" />
5
5
  </div>
@@ -18,20 +18,12 @@
18
18
  :placeholder="filter.placeholder"
19
19
  />
20
20
  </label>
21
- <label v-else-if="filter.type === 'options'" class="bk-form-select">
22
- <div v-if="!filterValues[filter.name]">
23
- {{ filter.label }}
24
- </div>
25
- <select v-model="filterValues[filter.name]">
26
- <option
27
- v-for="option in filter.options"
28
- :key="option.value"
29
- :value="option.value"
30
- >
31
- {{ option.label }}
32
- </option>
33
- </select>
34
- </label>
21
+ <FilterSelect
22
+ v-else-if="filter.type === 'options'"
23
+ v-model="filterValues[filter.name]"
24
+ :label="filter.label"
25
+ :options="filter.options"
26
+ />
35
27
  <FormToggle
36
28
  v-else-if="filter.type === 'checkbox'"
37
29
  v-model="filterValues[filter.name]"
@@ -41,7 +33,7 @@
41
33
  </div>
42
34
  <div
43
35
  ref="listEl"
44
- class="bk-media-library-items bk-scrollbar-light"
36
+ class="bk-media-library-items"
45
37
  :class="'bk-is-' + listView"
46
38
  >
47
39
  <Sortli no-transition :get-drag-items="getDragItems" :build-item>
@@ -85,6 +77,7 @@ import {
85
77
  FormToggle
86
78
  } from "#blokkli/editor/components";
87
79
  import Item from "./Item.vue";
80
+ import FilterSelect from "./FilterSelect/index.vue";
88
81
  import { falsy } from "#blokkli/helpers";
89
82
  import { onBlokkliEvent } from "#blokkli/editor/composables";
90
83
  defineProps({
@@ -139,6 +132,7 @@ const toggleListView = () => {
139
132
  listView.value = listView.value === "grid" ? "horizontal" : "grid";
140
133
  };
141
134
  const filterValues = ref({});
135
+ const defaultsApplied = ref(false);
142
136
  watch(key, () => {
143
137
  page.value = 0;
144
138
  });
@@ -162,6 +156,17 @@ const items = computed(() => data.value?.items || []);
162
156
  const filters = computed(() => {
163
157
  return data.value?.filters ?? [];
164
158
  });
159
+ watch(filters, (newFilters) => {
160
+ if (defaultsApplied.value || !newFilters.length) {
161
+ return;
162
+ }
163
+ defaultsApplied.value = true;
164
+ for (const filter of newFilters) {
165
+ if ("defaultValue" in filter && filter.defaultValue !== void 0 && filterValues.value[filter.name] === void 0) {
166
+ filterValues.value[filter.name] = filter.defaultValue;
167
+ }
168
+ }
169
+ });
165
170
  const firstSelectedBundle = computed(() => {
166
171
  if (selected.value.length) {
167
172
  const item = items.value.find((v) => v.mediaId === selected.value[0]);
@@ -9,7 +9,10 @@ function getControlState(e) {
9
9
  if ("code" in e && e.code === "CapsLock") {
10
10
  return true;
11
11
  }
12
- return e.getModifierState("Control") || e.getModifierState("Meta");
12
+ if ("getModifierState" in e) {
13
+ return e.getModifierState("Control") || e.getModifierState("Meta");
14
+ }
15
+ return false;
13
16
  }
14
17
  export default function(eventBus) {
15
18
  const isPressingControl = ref(false);
@@ -19,8 +22,8 @@ export default function(eventBus) {
19
22
  const keyboardLocks = ref([]);
20
23
  const keyboardLocked = computed(() => !!keyboardLocks.value.length);
21
24
  const onKeyUp = (e) => {
22
- isPressingControl.value = e.getModifierState("Control") || e.getModifierState("Meta");
23
- isPressingShift.value = e.getModifierState("Shift");
25
+ isPressingControl.value = e.getModifierState ? e.getModifierState("Control") || e.getModifierState("Meta") : false;
26
+ isPressingShift.value = e.getModifierState ? e.getModifierState("Shift") : false;
24
27
  if (e.code === "Space") {
25
28
  isPressingSpace.value = false;
26
29
  }
@@ -33,8 +36,8 @@ export default function(eventBus) {
33
36
  return;
34
37
  }
35
38
  isPressingControl.value = getControlState(e);
36
- isPressingShift.value = e.getModifierState("Shift");
37
- if (!isPressingSpace.value) {
39
+ isPressingShift.value = e.getModifierState ? e.getModifierState("Shift") : false;
40
+ if (!isPressingSpace.value && e.code) {
38
41
  eventBus.emit("keyPressed", {
39
42
  code: e.key,
40
43
  shift: e.shiftKey,
@@ -1267,10 +1267,6 @@
1267
1267
  "source": "Edit chart...",
1268
1268
  "translation": "Diagramm bearbeiten..."
1269
1269
  },
1270
- "chartsEditorSaveError": {
1271
- "source": "The chart could not be saved.",
1272
- "translation": "Das Diagramm konnte nicht gespeichert werden."
1273
- },
1274
1270
  "chartsEditorTitle": {
1275
1271
  "source": "Edit chart",
1276
1272
  "translation": "Diagramm bearbeiten"
@@ -1523,6 +1519,10 @@
1523
1519
  "source": "Shows all comments for the current page.",
1524
1520
  "translation": "Zeigt alle Kommentare auf der aktuellen Seite an."
1525
1521
  },
1522
+ "complexOptionsSaveError": {
1523
+ "source": "The data could not be saved.",
1524
+ "translation": ""
1525
+ },
1526
1526
  "conversionsConvertTo": {
1527
1527
  "source": "Convert to: @bundle",
1528
1528
  "translation": "Konvertieren zu: @bundle"
@@ -1851,14 +1851,6 @@
1851
1851
  "source": "Changelog",
1852
1852
  "translation": "Änderungsprotokoll"
1853
1853
  },
1854
- "feature_charts_description": {
1855
- "source": "Add and edit interactive charts.",
1856
- "translation": "Interaktive Diagramme hinzufügen und bearbeiten."
1857
- },
1858
- "feature_charts_label": {
1859
- "source": "Charts",
1860
- "translation": "Diagramme"
1861
- },
1862
1854
  "feature_clipboard_description": {
1863
1855
  "source": "Provides clipboard integration to copy/paste existing blocks or paste supported clipboard content like text or images.",
1864
1856
  "translation": "Stellt Zwischenablage-Integration bereit zum Kopieren/Einfügen von bestehenden Blöcken oder zum Einfügen von unterstützten Inhalten wie Text oder Bildern."
@@ -1883,6 +1875,14 @@
1883
1875
  "source": "Comments",
1884
1876
  "translation": "Kommentare"
1885
1877
  },
1878
+ "feature_complex-options_description": {
1879
+ "source": "Edit complex option types such as charts.",
1880
+ "translation": ""
1881
+ },
1882
+ "feature_complex-options_label": {
1883
+ "source": "Complex Options",
1884
+ "translation": ""
1885
+ },
1886
1886
  "feature_conversions_description": {
1887
1887
  "source": "Provides block actions to convert one or more blocks to a different bundle.",
1888
1888
  "translation": "Stellt Block-Aktionen bereit, um einen oder mehrere Blöcke in einen anderen Typ zu konvertieren."
@@ -2259,6 +2259,14 @@
2259
2259
  "source": "This field is required",
2260
2260
  "translation": "Feld darf nicht leer sein"
2261
2261
  },
2262
+ "filterSelectNoResults": {
2263
+ "source": "No results",
2264
+ "translation": "Keine Ergebnisse"
2265
+ },
2266
+ "filterSelectSearch": {
2267
+ "source": "Search...",
2268
+ "translation": "Suchen..."
2269
+ },
2262
2270
  "fragmentsAddFragmentAction": {
2263
2271
  "source": "Fragment",
2264
2272
  "translation": "Fragment"
@@ -2460,8 +2468,8 @@
2460
2468
  "translation": "Zurück zur Seite"
2461
2469
  },
2462
2470
  "libraryItemEditOverlayBackWithPage": {
2463
- "source": "Back to \"@label\"",
2464
- "translation": "Zurück zu «@label»"
2471
+ "source": "Save and go back to \"@label\"",
2472
+ "translation": "Speichern und zurück zu «@label»"
2465
2473
  },
2466
2474
  "libraryItemEditOverlayTitle": {
2467
2475
  "source": "Edit reusable block",