@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.
- package/dist/chunks/tailwindConfig.mjs +222 -0
- package/dist/global/types/definitions.d.ts +1 -1
- package/dist/module.d.mts +2 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +164 -16
- package/dist/modules/agent/index.d.mts +1 -1
- package/dist/modules/agent/index.mjs +6 -29
- package/dist/modules/charts/index.d.mts +1 -1
- package/dist/modules/charts/index.mjs +3 -3
- package/dist/modules/charts/runtime/features/charts/Editor/index.d.vue.ts +1 -0
- package/dist/modules/charts/runtime/features/charts/Editor/index.vue +50 -53
- package/dist/modules/charts/runtime/features/charts/Editor/index.vue.d.ts +1 -0
- package/dist/modules/drupal/index.d.mts +1 -1
- package/dist/modules/drupal/index.mjs +1 -1
- package/dist/modules/index.d.mts +7 -0
- package/dist/{shared/editor.CGf7C_0M.mjs → modules/index.mjs} +1 -1
- package/dist/modules/table-of-contents/index.d.mts +1 -1
- package/dist/modules/table-of-contents/index.mjs +1 -1
- package/dist/modules/tailwind/index.d.mts +5 -0
- package/dist/modules/tailwind/index.mjs +2 -0
- package/dist/runtime/editor/components/NestedEditorOverlay/index.vue +4 -4
- package/dist/runtime/editor/css/output.css +1 -1
- package/dist/runtime/editor/features/add-list/index.vue +1 -1
- package/dist/runtime/editor/features/anchors/Overlay/index.vue +25 -7
- package/dist/runtime/editor/features/changelog/changelog.json +9 -1
- package/dist/runtime/editor/features/complex-options/index.vue +98 -0
- package/dist/runtime/editor/features/dragging-overlay/index.vue +29 -13
- package/dist/runtime/editor/features/media-library/Library/FilterSelect/index.d.vue.ts +15 -0
- package/dist/runtime/editor/features/media-library/Library/FilterSelect/index.vue +168 -0
- package/dist/runtime/editor/features/media-library/Library/FilterSelect/index.vue.d.ts +15 -0
- package/dist/runtime/editor/features/media-library/Library/index.vue +21 -16
- package/dist/runtime/editor/providers/keyboard.js +8 -5
- package/dist/runtime/editor/translations/de.json +22 -14
- package/dist/runtime/editor/translations/fr.json +21 -13
- package/dist/runtime/editor/translations/gsw_CH.json +459 -451
- package/dist/runtime/editor/translations/it.json +21 -13
- package/dist/shared/{editor.BVregnEC.d.mts → editor.DsGJIlGn.d.mts} +19 -2
- package/dist/types.d.mts +1 -1
- package/package.json +36 -1
- package/dist/modules/charts/runtime/features/charts/index.vue +0 -72
- /package/dist/{modules/charts/runtime/features/charts → runtime/editor/features/complex-options}/index.d.vue.ts +0 -0
- /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,
|
|
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,
|
|
19
|
-
|
|
19
|
+
const { $t, adapter, ui } = useBlokkli();
|
|
20
|
+
function getAnchorItems() {
|
|
20
21
|
const anchorItems = [];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
242
|
-
if (!editableField) {
|
|
252
|
+
if (!dropResult?.focusEditable) {
|
|
243
253
|
return;
|
|
244
254
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
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
|
-
|
|
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": "
|
|
2464
|
-
"translation": "
|
|
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",
|