@abraca/nuxt 1.6.0 → 1.8.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.d.mts +6 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +16 -2
- package/dist/runtime/assets/sources.css +1 -0
- package/dist/runtime/components/ADocumentTree.d.vue.ts +11 -1
- package/dist/runtime/components/ADocumentTree.vue +13 -6
- package/dist/runtime/components/ADocumentTree.vue.d.ts +11 -1
- package/dist/runtime/components/renderers/AChecklistRenderer.vue +22 -4
- package/dist/runtime/components/renderers/ADashboardRenderer.vue +4 -2
- package/dist/runtime/components/renderers/AGalleryRenderer.vue +97 -70
- package/dist/runtime/components/renderers/AGraphRenderer.vue +209 -58
- package/dist/runtime/components/renderers/AKanbanRenderer.vue +145 -34
- package/dist/runtime/components/renderers/AMediaRenderer.vue +27 -17
- package/dist/runtime/components/renderers/AOutlineRenderer.vue +38 -23
- package/dist/runtime/components/renderers/ASlidesRenderer.d.vue.ts +21 -0
- package/dist/runtime/components/renderers/ASlidesRenderer.vue +591 -0
- package/dist/runtime/components/renderers/ASlidesRenderer.vue.d.ts +21 -0
- package/dist/runtime/components/renderers/ASpatialRenderer.vue +23 -0
- package/dist/runtime/components/renderers/ATableRenderer.vue +20 -391
- package/dist/runtime/components/renderers/gallery/AGalleryItemCard.d.vue.ts +40 -0
- package/dist/runtime/components/renderers/gallery/AGalleryItemCard.vue +227 -0
- package/dist/runtime/components/renderers/gallery/AGalleryItemCard.vue.d.ts +40 -0
- package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.d.vue.ts +16 -0
- package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.vue +66 -0
- package/dist/runtime/components/renderers/spatial/SpatialTransformInputs.vue.d.ts +16 -0
- package/dist/runtime/components/renderers/table/ATableFlatMode.d.vue.ts +2 -0
- package/dist/runtime/components/renderers/table/ATableFlatMode.vue +184 -21
- package/dist/runtime/components/renderers/table/ATableFlatMode.vue.d.ts +2 -0
- package/dist/runtime/components/renderers/table/ATableHierarchyMode.d.vue.ts +26 -0
- package/dist/runtime/components/renderers/table/ATableHierarchyMode.vue +662 -0
- package/dist/runtime/components/renderers/table/ATableHierarchyMode.vue.d.ts +26 -0
- package/dist/runtime/composables/useAwareness.js +14 -3
- package/dist/runtime/composables/useBackgroundSync.js +19 -1
- package/dist/runtime/composables/useFileIndex.js +38 -17
- package/dist/runtime/composables/useSearchIndex.js +41 -16
- package/dist/runtime/composables/useSlidesNavigation.d.ts +45 -0
- package/dist/runtime/composables/useSlidesNavigation.js +185 -0
- package/dist/runtime/composables/useYDoc.d.ts +1 -1
- package/dist/runtime/composables/useYDoc.js +47 -9
- package/dist/runtime/locale.d.ts +38 -0
- package/dist/runtime/locale.js +41 -3
- package/dist/runtime/utils/docTypes.js +17 -0
- package/package.json +3 -3
package/dist/module.d.mts
CHANGED
|
@@ -40,6 +40,7 @@ declare module '@nuxt/schema' {
|
|
|
40
40
|
chat?: boolean;
|
|
41
41
|
spatial?: boolean;
|
|
42
42
|
media?: boolean;
|
|
43
|
+
slides?: boolean;
|
|
43
44
|
};
|
|
44
45
|
locale: AbracadabraLocale;
|
|
45
46
|
auth: {
|
|
@@ -161,6 +162,11 @@ interface ModuleOptions {
|
|
|
161
162
|
* Default: false.
|
|
162
163
|
*/
|
|
163
164
|
media?: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Register slides presentation renderer with two-axis navigation.
|
|
167
|
+
* Default: true (lightweight, no extra deps).
|
|
168
|
+
*/
|
|
169
|
+
slides?: boolean;
|
|
164
170
|
};
|
|
165
171
|
/**
|
|
166
172
|
* Automatically add Vite resolve.dedupe entries for ProseMirror, TipTap, and Yjs.
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -31,7 +31,8 @@ const module$1 = defineNuxtModule({
|
|
|
31
31
|
webrtc: false,
|
|
32
32
|
chat: false,
|
|
33
33
|
spatial: false,
|
|
34
|
-
media: false
|
|
34
|
+
media: false,
|
|
35
|
+
slides: true
|
|
35
36
|
},
|
|
36
37
|
addViteDedupe: true,
|
|
37
38
|
prefix: "",
|
|
@@ -241,7 +242,20 @@ const module$1 = defineNuxtModule({
|
|
|
241
242
|
addComponentsDir({
|
|
242
243
|
path: resolver.resolve("./runtime/components/renderers"),
|
|
243
244
|
prefix: options.prefix ?? "",
|
|
244
|
-
pathPrefix: false
|
|
245
|
+
pathPrefix: false,
|
|
246
|
+
// Subfolders contain internal sub-components that should NOT be auto-imported —
|
|
247
|
+
// they're imported explicitly by their parent renderer via relative paths.
|
|
248
|
+
ignore: [
|
|
249
|
+
"**/calendar/**",
|
|
250
|
+
"**/table/**",
|
|
251
|
+
"**/timeline/**",
|
|
252
|
+
"**/media/**",
|
|
253
|
+
"**/spatial/**",
|
|
254
|
+
"**/gallery/**",
|
|
255
|
+
"**/sheets/**",
|
|
256
|
+
"**/slides/**",
|
|
257
|
+
"**/chart/**"
|
|
258
|
+
]
|
|
245
259
|
});
|
|
246
260
|
}
|
|
247
261
|
addComponentsDir({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@source "../components/**/*.{vue,js,ts,mjs}";@source "../extensions/**/*.{vue,js,ts,mjs}";
|
|
@@ -3,7 +3,11 @@ type __VLS_Props = {
|
|
|
3
3
|
editable?: boolean;
|
|
4
4
|
selectedId?: string | null;
|
|
5
5
|
};
|
|
6
|
-
declare
|
|
6
|
+
declare var __VLS_39: {};
|
|
7
|
+
type __VLS_Slots = {} & {
|
|
8
|
+
'toolbar-start'?: (props: typeof __VLS_39) => any;
|
|
9
|
+
};
|
|
10
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
7
11
|
handleExternalDrop: (e: DragEvent, parentId?: string | null) => Promise<void>;
|
|
8
12
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
13
|
create: (parentId: string | null) => any;
|
|
@@ -14,5 +18,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
|
14
18
|
}>, {
|
|
15
19
|
editable: boolean;
|
|
16
20
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
17
22
|
declare const _default: typeof __VLS_export;
|
|
18
23
|
export default _default;
|
|
24
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
25
|
+
new (): {
|
|
26
|
+
$slots: S;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -1031,10 +1031,9 @@ defineExpose({
|
|
|
1031
1031
|
</div>
|
|
1032
1032
|
</div>
|
|
1033
1033
|
|
|
1034
|
-
<
|
|
1034
|
+
<div
|
|
1035
1035
|
v-else-if="!collapsed"
|
|
1036
|
-
|
|
1037
|
-
class="relative transition-colors duration-150 outline-none"
|
|
1036
|
+
class="flex flex-col min-h-0"
|
|
1038
1037
|
:class="externalDragActive ? 'ring-2 ring-inset ring-(--ui-primary)/30 rounded-(--ui-radius) bg-(--ui-primary)/3' : ''"
|
|
1039
1038
|
tabindex="0"
|
|
1040
1039
|
role="tree"
|
|
@@ -1045,9 +1044,14 @@ defineExpose({
|
|
|
1045
1044
|
@dragover="onTreeDragOver"
|
|
1046
1045
|
@drop.prevent="onOuterDrop"
|
|
1047
1046
|
>
|
|
1047
|
+
<UContextMenu :items="treeAreaMenuItems()">
|
|
1048
|
+
<div class="flex flex-col flex-1 min-h-0 overflow-hidden relative transition-colors duration-150 outline-none">
|
|
1048
1049
|
<!-- Header -->
|
|
1049
|
-
<div class="flex items-center justify-between px-3 py-1">
|
|
1050
|
-
<
|
|
1050
|
+
<div class="flex items-center justify-between px-3 py-1 gap-1">
|
|
1051
|
+
<div class="flex items-center gap-1 min-w-0">
|
|
1052
|
+
<slot name="toolbar-start" />
|
|
1053
|
+
<span class="text-[11px] font-medium text-(--ui-text-dimmed) uppercase tracking-wider truncate">Pages</span>
|
|
1054
|
+
</div>
|
|
1051
1055
|
<div class="flex items-center gap-0">
|
|
1052
1056
|
<UTooltip :text="allExpanded ? 'Collapse all' : 'Expand all'">
|
|
1053
1057
|
<UButton
|
|
@@ -1466,6 +1470,9 @@ defineExpose({
|
|
|
1466
1470
|
</template>
|
|
1467
1471
|
</ClientOnly>
|
|
1468
1472
|
|
|
1473
|
+
</div>
|
|
1474
|
+
</UContextMenu>
|
|
1475
|
+
|
|
1469
1476
|
<!-- Overlay slideover -->
|
|
1470
1477
|
<ANodePanel
|
|
1471
1478
|
:node-id="overlayNodeId"
|
|
@@ -1474,7 +1481,7 @@ defineExpose({
|
|
|
1474
1481
|
:doc-type="treeMap.get(overlayNodeId ?? '')?.type"
|
|
1475
1482
|
@close="closeOverlay"
|
|
1476
1483
|
/>
|
|
1477
|
-
</
|
|
1484
|
+
</div>
|
|
1478
1485
|
</template>
|
|
1479
1486
|
|
|
1480
1487
|
<style scoped>
|
|
@@ -3,7 +3,11 @@ type __VLS_Props = {
|
|
|
3
3
|
editable?: boolean;
|
|
4
4
|
selectedId?: string | null;
|
|
5
5
|
};
|
|
6
|
-
declare
|
|
6
|
+
declare var __VLS_39: {};
|
|
7
|
+
type __VLS_Slots = {} & {
|
|
8
|
+
'toolbar-start'?: (props: typeof __VLS_39) => any;
|
|
9
|
+
};
|
|
10
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
7
11
|
handleExternalDrop: (e: DragEvent, parentId?: string | null) => Promise<void>;
|
|
8
12
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
13
|
create: (parentId: string | null) => any;
|
|
@@ -14,5 +18,11 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
|
14
18
|
}>, {
|
|
15
19
|
editable: boolean;
|
|
16
20
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
21
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
17
22
|
declare const _default: typeof __VLS_export;
|
|
18
23
|
export default _default;
|
|
24
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
25
|
+
new (): {
|
|
26
|
+
$slots: S;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -49,8 +49,14 @@ const flatItems = computed(() => {
|
|
|
49
49
|
walk(null, 0);
|
|
50
50
|
return result;
|
|
51
51
|
});
|
|
52
|
-
const filterMode =
|
|
53
|
-
|
|
52
|
+
const filterMode = computed({
|
|
53
|
+
get: () => tree.treeMap.data?.[props.docId]?.meta?.checklistFilter ?? "all",
|
|
54
|
+
set: (v) => tree.updateMeta(props.docId, { checklistFilter: v })
|
|
55
|
+
});
|
|
56
|
+
const sortMode = computed({
|
|
57
|
+
get: () => tree.treeMap.data?.[props.docId]?.meta?.checklistSort ?? "manual",
|
|
58
|
+
set: (v) => tree.updateMeta(props.docId, { checklistSort: v })
|
|
59
|
+
});
|
|
54
60
|
function isDescendantOf(item, ancestorId) {
|
|
55
61
|
let current = item;
|
|
56
62
|
while (current) {
|
|
@@ -88,6 +94,13 @@ const visibleItems = computed(() => {
|
|
|
88
94
|
});
|
|
89
95
|
const totalCount = computed(() => flatItems.value.length);
|
|
90
96
|
const checkedCount = computed(() => flatItems.value.filter((i) => i.checked).length);
|
|
97
|
+
function isIndeterminate(item) {
|
|
98
|
+
if (!item.hasChildren) return false;
|
|
99
|
+
if (item.checked) return false;
|
|
100
|
+
const descendants = flatItems.value.filter((i) => isDescendantOf(i, item.id));
|
|
101
|
+
if (descendants.length === 0) return false;
|
|
102
|
+
return descendants.some((d) => d.checked);
|
|
103
|
+
}
|
|
91
104
|
const percent = computed(
|
|
92
105
|
() => totalCount.value === 0 ? 0 : Math.round(checkedCount.value / totalCount.value * 100)
|
|
93
106
|
);
|
|
@@ -465,10 +478,10 @@ defineExpose({ connectedUsers });
|
|
|
465
478
|
/>
|
|
466
479
|
</span>
|
|
467
480
|
|
|
468
|
-
<!-- Checkbox -->
|
|
481
|
+
<!-- Checkbox (supports indeterminate state for parents with partial completion) -->
|
|
469
482
|
<button
|
|
470
483
|
class="shrink-0 size-4 rounded border flex items-center justify-center transition-colors"
|
|
471
|
-
:class="item.checked ? 'bg-(--ui-primary) border-(--ui-primary)' : 'border-(--ui-border) hover:border-(--ui-primary)'"
|
|
484
|
+
:class="item.checked ? 'bg-(--ui-primary) border-(--ui-primary)' : isIndeterminate(item) ? 'bg-(--ui-primary)/40 border-(--ui-primary)/60' : 'border-(--ui-border) hover:border-(--ui-primary)'"
|
|
472
485
|
:disabled="!editable"
|
|
473
486
|
@click.stop="editable && tree.updateMeta(item.id, { checked: !item.checked })"
|
|
474
487
|
>
|
|
@@ -477,6 +490,11 @@ defineExpose({ connectedUsers });
|
|
|
477
490
|
name="i-lucide-check"
|
|
478
491
|
class="size-2.5 text-white"
|
|
479
492
|
/>
|
|
493
|
+
<UIcon
|
|
494
|
+
v-else-if="isIndeterminate(item)"
|
|
495
|
+
name="i-lucide-minus"
|
|
496
|
+
class="size-2.5 text-white"
|
|
497
|
+
/>
|
|
480
498
|
</button>
|
|
481
499
|
|
|
482
500
|
<!-- Label / inline edit -->
|
|
@@ -88,6 +88,7 @@ const widgetLoadingIds = ref(/* @__PURE__ */ new Set());
|
|
|
88
88
|
const widgetErrors = reactive({});
|
|
89
89
|
async function loadWidgetProvider(childId) {
|
|
90
90
|
if (widgetProviders[childId] || widgetLoadingIds.value.has(childId)) return;
|
|
91
|
+
if (!childProviderRef.value) return;
|
|
91
92
|
widgetLoadingIds.value.add(childId);
|
|
92
93
|
delete widgetErrors[childId];
|
|
93
94
|
try {
|
|
@@ -118,8 +119,9 @@ onErrorCaptured((err) => {
|
|
|
118
119
|
}
|
|
119
120
|
});
|
|
120
121
|
watch(
|
|
121
|
-
children,
|
|
122
|
-
(items) => {
|
|
122
|
+
[children, childProviderRef],
|
|
123
|
+
([items]) => {
|
|
124
|
+
if (!childProviderRef.value) return;
|
|
123
125
|
items.forEach((item) => {
|
|
124
126
|
const mode = item.meta?.deskMode;
|
|
125
127
|
if ((mode === "widget-sm" || mode === "widget-lg") && !widgetProviders[item.id]) {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { ref, computed, onBeforeUnmount } from "vue";
|
|
2
|
+
import { ref, computed, onBeforeUnmount, watch } from "vue";
|
|
3
|
+
import { useEventListener } from "@vueuse/core";
|
|
3
4
|
import { useRuntimeConfig } from "#imports";
|
|
4
5
|
import { useRendererBase } from "../../composables/useRendererBase";
|
|
5
6
|
import { useTouchDrag } from "../../composables/useTouchDrag";
|
|
6
7
|
import { useNodePanel } from "../../composables/useNodePanel";
|
|
7
8
|
import { getMetaColor } from "../../utils/getMetaColor";
|
|
8
9
|
import { DEFAULT_LOCALE } from "../../locale";
|
|
10
|
+
import AGalleryItemCard from "./gallery/AGalleryItemCard.vue";
|
|
9
11
|
const props = defineProps({
|
|
10
12
|
docId: { type: String, required: true },
|
|
11
13
|
childProvider: { type: null, required: true },
|
|
@@ -29,7 +31,27 @@ const {
|
|
|
29
31
|
closePanel
|
|
30
32
|
} = useNodePanel(childProviderRef);
|
|
31
33
|
const myClientId = computed(() => props.childProvider?.awareness?.clientID ?? 0);
|
|
32
|
-
const
|
|
34
|
+
const rawItems = computed(() => tree.childrenOf(null));
|
|
35
|
+
const items = computed(() => {
|
|
36
|
+
const raw = rawItems.value;
|
|
37
|
+
const mode = gallerySortBy.value;
|
|
38
|
+
if (mode === "manual") return raw;
|
|
39
|
+
const arr = [...raw];
|
|
40
|
+
if (mode === "name") {
|
|
41
|
+
return arr.sort((a, b) => (a.label ?? "").localeCompare(b.label ?? ""));
|
|
42
|
+
}
|
|
43
|
+
if (mode === "rating") {
|
|
44
|
+
return arr.sort((a, b) => (b.meta?.rating ?? 0) - (a.meta?.rating ?? 0));
|
|
45
|
+
}
|
|
46
|
+
return arr.sort((a, b) => {
|
|
47
|
+
const da = a.meta?.datetimeStart ?? a.meta?.dateStart ?? a.meta?.dateTaken;
|
|
48
|
+
const db = b.meta?.datetimeStart ?? b.meta?.dateStart ?? b.meta?.dateTaken;
|
|
49
|
+
if (!da && !db) return 0;
|
|
50
|
+
if (!da) return 1;
|
|
51
|
+
if (!db) return -1;
|
|
52
|
+
return new Date(da).getTime() - new Date(db).getTime();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
33
55
|
const renameId = ref(null);
|
|
34
56
|
const renameValue = ref("");
|
|
35
57
|
let renameCooldown = false;
|
|
@@ -109,6 +131,30 @@ const gridClass = computed(() => {
|
|
|
109
131
|
return "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4";
|
|
110
132
|
}
|
|
111
133
|
});
|
|
134
|
+
const docMeta = computed(() => tree.treeMap.data?.[props.docId]?.meta);
|
|
135
|
+
const ASPECT_MAP = {
|
|
136
|
+
square: "1 / 1",
|
|
137
|
+
"4:3": "4 / 3",
|
|
138
|
+
"3:2": "3 / 2",
|
|
139
|
+
"16:9": "16 / 9",
|
|
140
|
+
free: "auto"
|
|
141
|
+
};
|
|
142
|
+
const galleryAspect = computed(() => {
|
|
143
|
+
const v = docMeta.value?.galleryAspect ?? "4:3";
|
|
144
|
+
return ASPECT_MAP[v] ?? ASPECT_MAP["4:3"];
|
|
145
|
+
});
|
|
146
|
+
const cardStyle = computed(() => docMeta.value?.galleryCardStyle ?? "default");
|
|
147
|
+
const showLabels = computed(() => docMeta.value?.galleryShowLabels ?? true);
|
|
148
|
+
const galleryColumns = computed(() => {
|
|
149
|
+
const v = docMeta.value?.galleryColumns;
|
|
150
|
+
return typeof v === "number" && v >= 1 && v <= 6 ? v : null;
|
|
151
|
+
});
|
|
152
|
+
const gridStyle = computed(
|
|
153
|
+
() => galleryColumns.value ? { gridTemplateColumns: `repeat(${galleryColumns.value}, minmax(0, 1fr))` } : void 0
|
|
154
|
+
);
|
|
155
|
+
const gallerySortBy = computed(
|
|
156
|
+
() => docMeta.value?.gallerySortBy ?? "manual"
|
|
157
|
+
);
|
|
112
158
|
const gridSizeItems = computed(() => [
|
|
113
159
|
[
|
|
114
160
|
{ label: "Small", icon: "i-lucide-grid-3x3", onSelect: () => {
|
|
@@ -139,6 +185,35 @@ function openLightbox(item) {
|
|
|
139
185
|
function closeLightbox() {
|
|
140
186
|
lightboxItem.value = null;
|
|
141
187
|
}
|
|
188
|
+
function lightboxNavigate(direction) {
|
|
189
|
+
if (!lightboxItem.value) return;
|
|
190
|
+
const list = items.value.filter((i) => i.meta?.coverUploadId);
|
|
191
|
+
const idx = list.findIndex((i) => i.id === lightboxItem.value.id);
|
|
192
|
+
if (idx === -1) return;
|
|
193
|
+
const next = list[(idx + direction + list.length) % list.length];
|
|
194
|
+
lightboxItem.value = {
|
|
195
|
+
id: next.id,
|
|
196
|
+
label: next.label,
|
|
197
|
+
uploadId: next.meta.coverUploadId,
|
|
198
|
+
mimeType: next.meta.coverMimeType
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
watch(lightboxItem, (v) => {
|
|
202
|
+
if (v) return;
|
|
203
|
+
});
|
|
204
|
+
useEventListener(typeof window !== "undefined" ? window : null, "keydown", (e) => {
|
|
205
|
+
if (!lightboxItem.value) return;
|
|
206
|
+
if (e.key === "ArrowLeft") {
|
|
207
|
+
e.preventDefault();
|
|
208
|
+
lightboxNavigate(-1);
|
|
209
|
+
} else if (e.key === "ArrowRight") {
|
|
210
|
+
e.preventDefault();
|
|
211
|
+
lightboxNavigate(1);
|
|
212
|
+
} else if (e.key === "Escape") {
|
|
213
|
+
e.preventDefault();
|
|
214
|
+
closeLightbox();
|
|
215
|
+
}
|
|
216
|
+
});
|
|
142
217
|
function itemBorderColor(item) {
|
|
143
218
|
const c = getMetaColor(item.meta);
|
|
144
219
|
return c ? `border-color: ${c}` : void 0;
|
|
@@ -205,84 +280,36 @@ defineExpose({ connectedUsers });
|
|
|
205
280
|
name="gallery"
|
|
206
281
|
tag="div"
|
|
207
282
|
class="grid gap-3"
|
|
208
|
-
:class="gridClass"
|
|
283
|
+
:class="galleryColumns ? '' : gridClass"
|
|
284
|
+
:style="gridStyle"
|
|
209
285
|
>
|
|
210
286
|
<UContextMenu
|
|
211
287
|
v-for="item in items"
|
|
212
288
|
:key="item.id"
|
|
213
289
|
:items="editable ? itemMenuItems(item) : []"
|
|
214
290
|
>
|
|
215
|
-
<
|
|
216
|
-
|
|
217
|
-
:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
291
|
+
<AGalleryItemCard
|
|
292
|
+
:item="item"
|
|
293
|
+
:gallery-aspect="galleryAspect"
|
|
294
|
+
:card-style="cardStyle"
|
|
295
|
+
:show-labels="showLabels"
|
|
296
|
+
:is-dragging="dragId === item.id"
|
|
297
|
+
:is-drag-over="dragOverId === item.id"
|
|
298
|
+
:focusers="itemFocusers(item.id)"
|
|
299
|
+
:can-write="editable"
|
|
300
|
+
:rename-id="renameId"
|
|
301
|
+
:rename-value="renameValue"
|
|
225
302
|
:data-drag-id="item.id"
|
|
303
|
+
@update:rename-value="renameValue = $event"
|
|
304
|
+
@commit-rename="commitRename"
|
|
305
|
+
@cancel-rename="renameId = null"
|
|
226
306
|
@pointerdown="editable && handlePointerDown($event, item.id)"
|
|
227
307
|
@pointerenter="onItemPointerEnter(item.id)"
|
|
228
308
|
@pointerleave="onItemPointerLeave"
|
|
229
309
|
@click="openLightbox(item)"
|
|
310
|
+
@dblclick.stop="editable && startRename(item.id, item.label)"
|
|
230
311
|
>
|
|
231
|
-
|
|
232
|
-
<div
|
|
233
|
-
v-if="itemFocusers(item.id).length"
|
|
234
|
-
class="absolute top-1 right-1 z-10 flex gap-0.5"
|
|
235
|
-
>
|
|
236
|
-
<span
|
|
237
|
-
v-for="f in itemFocusers(item.id)"
|
|
238
|
-
:key="f.clientId"
|
|
239
|
-
class="text-[10px] leading-none px-1.5 py-0.5 rounded-full text-white truncate max-w-20"
|
|
240
|
-
:style="{ backgroundColor: f.user?.color ?? '#888' }"
|
|
241
|
-
>
|
|
242
|
-
{{ f.user?.name ?? "User" }}
|
|
243
|
-
</span>
|
|
244
|
-
</div>
|
|
245
|
-
|
|
246
|
-
<!-- Cover or placeholder -->
|
|
247
|
-
<div
|
|
248
|
-
class="aspect-[4/3] bg-(--ui-bg-elevated) flex items-center justify-center overflow-hidden"
|
|
249
|
-
:style="item.meta?.color ? { backgroundColor: item.meta.color + '22' } : {}"
|
|
250
|
-
>
|
|
251
|
-
<AGalleryCoverImage
|
|
252
|
-
v-if="item.meta?.coverUploadId"
|
|
253
|
-
:upload-id="item.meta.coverUploadId"
|
|
254
|
-
:doc-id="item.id"
|
|
255
|
-
:mime-type="item.meta.coverMimeType"
|
|
256
|
-
/>
|
|
257
|
-
<UIcon
|
|
258
|
-
v-else
|
|
259
|
-
:name="item.meta?.icon || 'i-lucide-file-text'"
|
|
260
|
-
class="size-8 text-(--ui-text-dimmed) opacity-40"
|
|
261
|
-
/>
|
|
262
|
-
</div>
|
|
263
|
-
|
|
264
|
-
<!-- Label -->
|
|
265
|
-
<div class="p-2 border-t border-(--ui-border) flex items-center justify-between gap-1">
|
|
266
|
-
<UInput
|
|
267
|
-
v-if="editable && renameId === item.id"
|
|
268
|
-
v-model="renameValue"
|
|
269
|
-
size="xs"
|
|
270
|
-
variant="none"
|
|
271
|
-
class="flex-1 -mx-1"
|
|
272
|
-
autofocus
|
|
273
|
-
@keydown.enter="commitRename"
|
|
274
|
-
@keydown.escape="renameId = null"
|
|
275
|
-
@blur="commitRename"
|
|
276
|
-
@click.stop
|
|
277
|
-
/>
|
|
278
|
-
<p
|
|
279
|
-
v-else
|
|
280
|
-
class="text-xs font-medium truncate"
|
|
281
|
-
@dblclick.stop="editable && startRename(item.id, item.label)"
|
|
282
|
-
>
|
|
283
|
-
{{ item.label }}
|
|
284
|
-
</p>
|
|
285
|
-
|
|
312
|
+
<template #actions>
|
|
286
313
|
<UDropdownMenu
|
|
287
314
|
v-if="editable"
|
|
288
315
|
:items="itemMenuItems(item)"
|
|
@@ -297,8 +324,8 @@ defineExpose({ connectedUsers });
|
|
|
297
324
|
@click.stop
|
|
298
325
|
/>
|
|
299
326
|
</UDropdownMenu>
|
|
300
|
-
</
|
|
301
|
-
</
|
|
327
|
+
</template>
|
|
328
|
+
</AGalleryItemCard>
|
|
302
329
|
</UContextMenu>
|
|
303
330
|
</TransitionGroup>
|
|
304
331
|
</div>
|