@blokkli/editor 1.1.3 → 1.2.0

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/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "blokkli",
3
3
  "configKey": "blokkli",
4
- "version": "1.1.3",
4
+ "version": "1.2.0",
5
5
  "compatibility": {
6
6
  "nuxt": "^3.12.0"
7
7
  },
package/dist/module.mjs CHANGED
@@ -7,7 +7,7 @@ import MagicString from 'magic-string';
7
7
  import { walk } from 'estree-walker-ts';
8
8
  import { BK_VISIBLE_LANGUAGES, BK_HIDDEN_GLOBALLY } from '../dist/runtime/helpers/symbols.js';
9
9
 
10
- const version = "1.1.3";
10
+ const version = "1.2.0";
11
11
 
12
12
  function sortObjectKeys(obj) {
13
13
  if (Array.isArray(obj)) {
@@ -850,6 +850,10 @@ const cancel$2 = {
850
850
  source: "Cancel",
851
851
  translation: "Abbrechen"
852
852
  };
853
+ const cancelSelection$2 = {
854
+ source: "Cancel selection",
855
+ translation: "Auswahl abbrechen"
856
+ };
853
857
  const clipboard$2 = {
854
858
  source: "Clipboard",
855
859
  translation: "Zwischenablage"
@@ -1689,6 +1693,7 @@ const de = {
1689
1693
  blockOption_bkHiddenGlobally_label: blockOption_bkHiddenGlobally_label$2,
1690
1694
  blockOption_bkVisibleLanguages_label: blockOption_bkVisibleLanguages_label$2,
1691
1695
  cancel: cancel$2,
1696
+ cancelSelection: cancelSelection$2,
1692
1697
  clipboard: clipboard$2,
1693
1698
  clipboardCopyShortcutHelp: clipboardCopyShortcutHelp$2,
1694
1699
  clipboardEmpty: clipboardEmpty$2,
@@ -2093,6 +2098,10 @@ const cancel$1 = {
2093
2098
  source: "Cancel",
2094
2099
  translation: "Annuler"
2095
2100
  };
2101
+ const cancelSelection$1 = {
2102
+ source: "Cancel selection",
2103
+ translation: ""
2104
+ };
2096
2105
  const clipboard$1 = {
2097
2106
  source: "Clipboard",
2098
2107
  translation: "Presse-papiers"
@@ -2932,6 +2941,7 @@ const fr = {
2932
2941
  blockOption_bkHiddenGlobally_label: blockOption_bkHiddenGlobally_label$1,
2933
2942
  blockOption_bkVisibleLanguages_label: blockOption_bkVisibleLanguages_label$1,
2934
2943
  cancel: cancel$1,
2944
+ cancelSelection: cancelSelection$1,
2935
2945
  clipboard: clipboard$1,
2936
2946
  clipboardCopyShortcutHelp: clipboardCopyShortcutHelp$1,
2937
2947
  clipboardEmpty: clipboardEmpty$1,
@@ -3336,6 +3346,10 @@ const cancel = {
3336
3346
  source: "Cancel",
3337
3347
  translation: "Annulla"
3338
3348
  };
3349
+ const cancelSelection = {
3350
+ source: "Cancel selection",
3351
+ translation: ""
3352
+ };
3339
3353
  const clipboard = {
3340
3354
  source: "Clipboard",
3341
3355
  translation: "Appunti"
@@ -4175,6 +4189,7 @@ const it = {
4175
4189
  blockOption_bkHiddenGlobally_label: blockOption_bkHiddenGlobally_label,
4176
4190
  blockOption_bkVisibleLanguages_label: blockOption_bkVisibleLanguages_label,
4177
4191
  cancel: cancel,
4192
+ cancelSelection: cancelSelection,
4178
4193
  clipboard: clipboard,
4179
4194
  clipboardCopyShortcutHelp: clipboardCopyShortcutHelp,
4180
4195
  clipboardEmpty: clipboardEmpty,
@@ -700,6 +700,32 @@ mutation pbAddEntityReference(
700
700
  }
701
701
  }
702
702
 
703
+ mutation pbAddEntityReferenceMultiple(
704
+ $langcode: String
705
+ $entityType: EntityType!
706
+ $entityUuid: String!
707
+ $references: [ParagraphsBlokkliAddEntityReferenceMultipleInput]!
708
+ $hostType: String!
709
+ $hostUuid: String!
710
+ $hostFieldName: String!
711
+ $afterUuid: String
712
+ ) {
713
+ state: paragraphsEditMutationState(
714
+ entityType: $entityType
715
+ entityUuid: $entityUuid
716
+ ) {
717
+ action: add_entity_reference_multiple(
718
+ references: $references
719
+ afterUuid: $afterUuid
720
+ hostType: $hostType
721
+ hostUuid: $hostUuid
722
+ hostFieldName: $hostFieldName
723
+ ) {
724
+ ...paragraphsBlokkliMutationResult
725
+ }
726
+ }
727
+ }
728
+
703
729
  query pbConfig($entityType: String!, $entityBundle: String!) {
704
730
  clipboards: pbGetSupportedClipboards {
705
731
  ...paragraphsBlokkliSupportedClipboard
@@ -491,6 +491,23 @@ export default defineBlokkliEditAdapter(
491
491
  afterUuid: e.preceedingUuid
492
492
  }).then(mapMutation);
493
493
  };
494
+ const mediaLibraryAddBlocks = (e) => {
495
+ return useGraphqlMutation("pbAddEntityReferenceMultiple", {
496
+ ...ctx.value,
497
+ references: e.items.map((item) => {
498
+ return {
499
+ targetId: item.mediaId,
500
+ targetType: "media",
501
+ targetBundle: item.mediaBundle,
502
+ paragraphBundle: item.itemBundle
503
+ };
504
+ }),
505
+ hostType: e.host.type,
506
+ hostUuid: e.host.uuid,
507
+ hostFieldName: e.host.fieldName,
508
+ afterUuid: e.preceedingUuid
509
+ }).then(mapMutation);
510
+ };
494
511
  const getContentSearchTabs = () => {
495
512
  return useGraphqlQuery("pbSearchTabs").then((v) => {
496
513
  return (v.data.tabs || []).reduce(
@@ -642,6 +659,7 @@ export default defineBlokkliEditAdapter(
642
659
  getDroppableFieldConfig,
643
660
  mediaLibraryGetResults,
644
661
  mediaLibraryAddBlock,
662
+ mediaLibraryAddBlocks,
645
663
  getContentSearchTabs,
646
664
  getContentSearchResults,
647
665
  addContentSearchItem,
@@ -26,6 +26,11 @@ export type MediaLibraryAddBlockEvent = {
26
26
  preceedingUuid?: string;
27
27
  item: DraggableMediaLibraryItem;
28
28
  };
29
+ export type MediaLibraryAddBlocksEvent = {
30
+ host: DraggableHostData;
31
+ preceedingUuid?: string;
32
+ items: DraggableMediaLibraryItem[];
33
+ };
29
34
  export type MediaLibraryReplaceMediaEvent = {
30
35
  /**
31
36
  * The UUID of the block on which the media was dropped.
@@ -343,6 +348,10 @@ export interface BlokkliAdapter<T> {
343
348
  * Create a new block from the given media library item.
344
349
  */
345
350
  mediaLibraryAddBlock?: (e: MediaLibraryAddBlockEvent) => Promise<MutationResponseLike<T>> | undefined;
351
+ /**
352
+ * Create new blocks from the given media library items.
353
+ */
354
+ mediaLibraryAddBlocks?: (e: MediaLibraryAddBlocksEvent) => Promise<MutationResponseLike<T>> | undefined;
346
355
  /**
347
356
  * Replace an existing media from a block with a new one.
348
357
  *
@@ -90,7 +90,9 @@ type FilteredItemType<T extends DraggableItem> = T extends {
90
90
  ? { itemType: 'existing'; items: T[] }
91
91
  : T extends { itemType: 'existing_structure' }
92
92
  ? { itemType: 'existing_structure'; items: T[] }
93
- : { itemType: T['itemType']; item: T }
93
+ : T extends { itemType: 'media_library' }
94
+ ? { itemType: 'media_library'; items: T[] }
95
+ : { itemType: T['itemType']; item: T }
94
96
 
95
97
  function filterItemType<T extends DraggableItem>(
96
98
  items: T[],
@@ -105,7 +107,11 @@ function filterItemType<T extends DraggableItem>(
105
107
 
106
108
  const itemType = items[0].itemType
107
109
 
108
- if (itemType === 'existing' || itemType === 'existing_structure') {
110
+ if (
111
+ itemType === 'existing' ||
112
+ itemType === 'existing_structure' ||
113
+ itemType === 'media_library'
114
+ ) {
109
115
  return { itemType, items } as any
110
116
  }
111
117
 
@@ -211,16 +217,24 @@ const onDropClipboardItem = async (
211
217
  }
212
218
 
213
219
  const onDropMediaLibraryItem = async (
214
- item: DraggableMediaLibraryItem,
220
+ items: DraggableMediaLibraryItem[],
215
221
  host: DraggableHostData,
216
222
  afterUuid?: string,
217
223
  ) => {
218
- if (adapter.mediaLibraryAddBlock) {
224
+ if (adapter.mediaLibraryAddBlock && items.length === 1) {
219
225
  await state.mutateWithLoadingState(() =>
220
226
  adapter.mediaLibraryAddBlock({
221
227
  preceedingUuid: afterUuid,
222
228
  host,
223
- item,
229
+ item: items[0],
230
+ }),
231
+ )
232
+ } else if (adapter.mediaLibraryAddBlocks && items.length > 1) {
233
+ await state.mutateWithLoadingState(() =>
234
+ adapter.mediaLibraryAddBlocks({
235
+ preceedingUuid: afterUuid,
236
+ host,
237
+ items,
224
238
  }),
225
239
  )
226
240
  }
@@ -279,7 +293,7 @@ const onDrop = (e: DropTargetEvent) => {
279
293
  } else if (typed.itemType === 'action') {
280
294
  onDropAction(typed.item, host, e.field, afterUuid)
281
295
  } else if (typed.itemType === 'media_library') {
282
- await onDropMediaLibraryItem(typed.item, host, afterUuid)
296
+ await onDropMediaLibraryItem(typed.items, host, afterUuid)
283
297
  }
284
298
 
285
299
  eventBus.emit('dragging:end')
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <div
3
+ :key="mediaId"
4
+ class="bk-media-library-items-item"
5
+ :class="{ 'bk-is-selected': isSelected }"
6
+ :data-sortli-id="'media_library_' + mediaId"
7
+ data-element-type="media_library"
8
+ :data-item-bundle="targetBundles[0]"
9
+ :data-media-id="mediaId"
10
+ :data-media-bundle="mediaBundle"
11
+ >
12
+ <div class="bk-media-library-items-item-box">
13
+ <label @click.stop>
14
+ <input v-model="selected" type="checkbox" :value="mediaId" />
15
+ </label>
16
+ <div class="bk-media-library-items-item-image">
17
+ <img :src="thumbnail" />
18
+ </div>
19
+ </div>
20
+ <div class="bk-media-library-items-item-text">
21
+ <h3>{{ label }}</h3>
22
+ <p>{{ context }}</p>
23
+ </div>
24
+ </div>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import type { BlokkliIcon } from '#blokkli/icons'
29
+ import { computed } from '#imports'
30
+
31
+ const props = defineProps<{
32
+ mediaId: string
33
+ label: string
34
+ context: string
35
+ targetBundles: string[]
36
+ thumbnail?: string
37
+ icon?: BlokkliIcon
38
+ mediaBundle?: string
39
+ }>()
40
+
41
+ const selected = defineModel<string[]>()
42
+
43
+ const isSelected = computed(() => selected.value?.includes(props.mediaId))
44
+ </script>
@@ -50,30 +50,24 @@
50
50
  class="bk-media-library-items bk-scrollbar-light"
51
51
  :class="[{ 'bk-is-sortli': isSortli }, 'bk-is-' + listView]"
52
52
  >
53
- <Component :is="isSortli ? Sortli : 'div'" no-transition>
54
- <div
53
+ <Sortli v-if="isSortli" no-transition :get-drag-items="getDragItems">
54
+ <Item
55
55
  v-for="item in items"
56
56
  :key="item.mediaId"
57
- class="bk-media-library-items-item"
58
- :class="{ 'bk-is-selected': modelValue === item.mediaId }"
59
- :data-sortli-id="'media_library_' + item.mediaId"
60
- data-element-type="media_library"
61
- :data-item-bundle="item.targetBundles[0]"
62
- :data-media-id="item.mediaId"
63
- :data-media-bundle="item.mediaBundle"
64
- @click="onClick(item.mediaId)"
65
- >
66
- <div>
67
- <div class="bk-media-library-items-item-image">
68
- <img :src="item.thumbnail" />
69
- </div>
70
- </div>
71
- <div class="bk-media-library-items-item-text">
72
- <h3>{{ item.label }}</h3>
73
- <p>{{ item.context }}</p>
74
- </div>
75
- </div>
76
- </Component>
57
+ v-bind="item"
58
+ v-model="selected"
59
+ />
60
+ </Sortli>
61
+
62
+ <div v-else>
63
+ <Item v-for="item in items" :key="item.mediaId" v-bind="item" />
64
+ </div>
65
+ </div>
66
+
67
+ <div v-if="selected.length" class="bk-media-library-cancel">
68
+ <button class="bk-button bk-is-primary" @click.prevent="selected = []">
69
+ {{ $t('cancelSelection', 'Cancel selection') }}
70
+ </button>
77
71
  </div>
78
72
 
79
73
  <div class="bk-pagination">
@@ -100,20 +94,60 @@ import {
100
94
  import { Sortli, Icon } from '#blokkli/components'
101
95
  import type { MediaLibraryFilter, MediaLibraryGetResults } from './../types'
102
96
  import type { BlokkliIcon } from '#blokkli/icons'
97
+ import Item from './Item.vue'
98
+ import type { DraggableItem, DraggableMediaLibraryItem } from '#blokkli/types'
99
+ import { buildDraggableItem, falsy } from '#blokkli/helpers'
100
+ import onBlokkliEvent from '#blokkli/helpers/composables/onBlokkliEvent'
103
101
 
104
- const props = defineProps<{
102
+ defineProps<{
105
103
  isSortli?: boolean
106
104
  modelValue?: string
107
105
  }>()
108
106
 
109
- const emit = defineEmits(['update:modelValue'])
107
+ const { adapter, storage, $t } = useBlokkli()
108
+
109
+ const selected = ref<string[]>([])
110
+ const listEl = ref<HTMLDivElement | null>(null)
111
+ const page = ref(0)
112
+ const key = computed(() => Object.values(filterValues.value).join(','))
113
+
114
+ function getDragItems(activeItem?: DraggableItem): DraggableItem[] | null {
115
+ if (!selected.value.length || !listEl.value) {
116
+ return null
117
+ }
118
+
119
+ const activeId =
120
+ activeItem?.itemType === 'media_library' ? activeItem.mediaId : null
121
+
122
+ const items: DraggableMediaLibraryItem[] = selected.value
123
+ .map((id) => {
124
+ const el = listEl.value?.querySelector(
125
+ `[data-sortli-id="media_library_${id}"]`,
126
+ )
127
+ if (!(el instanceof HTMLElement)) {
128
+ return null
129
+ }
130
+
131
+ const item = buildDraggableItem(el)
132
+ if (item?.itemType === 'media_library') {
133
+ return item
134
+ }
135
+
136
+ return null
137
+ })
138
+ .filter(falsy)
139
+
140
+ if (!activeId) {
141
+ return items
142
+ }
143
+
144
+ const activeIsInSelection = items.find((v) => v.mediaId === activeId)
110
145
 
111
- const onClick = (id: string) => {
112
- if (props.isSortli) {
113
- return
146
+ if (activeIsInSelection) {
147
+ return items
114
148
  }
115
149
 
116
- emit('update:modelValue', id)
150
+ return null
117
151
  }
118
152
 
119
153
  type RenderedFilter = {
@@ -121,8 +155,6 @@ type RenderedFilter = {
121
155
  filter: MediaLibraryFilter
122
156
  }
123
157
 
124
- const { adapter, storage } = useBlokkli()
125
-
126
158
  const listView = storage.use<'horizontal' | 'grid'>(
127
159
  'mediaLibraryListView',
128
160
  'grid',
@@ -142,10 +174,6 @@ const toggleListView = () => {
142
174
 
143
175
  const filterValues = ref<Record<string, any>>({})
144
176
 
145
- const listEl = ref<HTMLDivElement | null>(null)
146
- const page = ref(0)
147
- const key = computed(() => Object.values(filterValues.value).join(','))
148
-
149
177
  watch(key, () => {
150
178
  page.value = 0
151
179
  })
@@ -182,4 +210,8 @@ const perPage = computed(() => data.value?.perPage || 0)
182
210
  const totalPages = computed(() => {
183
211
  return Math.ceil(total.value / perPage.value)
184
212
  })
213
+
214
+ onBlokkliEvent('item:dropped', function () {
215
+ selected.value = []
216
+ })
185
217
  </script>
@@ -19,6 +19,10 @@ import {
19
19
 
20
20
  const { eventBus } = useBlokkli()
21
21
 
22
+ const props = defineProps<{
23
+ getDragItems?: (activeItem?: DraggableItem) => DraggableItem[] | null
24
+ }>()
25
+
22
26
  let pointerStartCoords: Coord | null = null
23
27
  let activeItem: DraggableItem | null = null
24
28
 
@@ -55,8 +59,9 @@ function onPointerMove(e: PointerEvent) {
55
59
  const distance = getDistance(coords, pointerStartCoords)
56
60
 
57
61
  if (distance > 7) {
62
+ const dragItems = props.getDragItems ? props.getDragItems(activeItem) : null
58
63
  eventBus.emit('dragging:start', {
59
- items: [activeItem],
64
+ items: dragItems && dragItems.length ? dragItems : [activeItem],
60
65
  coords,
61
66
  mode: 'mouse',
62
67
  })