@revenexx/studio-shared 0.3.2 → 0.4.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 +12 -1
- package/dist/runtime/components/Dashboard/DashboardConfirmDialog.d.vue.ts +16 -0
- package/dist/runtime/components/Dashboard/DashboardConfirmDialog.vue +39 -0
- package/dist/runtime/components/Dashboard/DashboardConfirmDialog.vue.d.ts +16 -0
- package/dist/runtime/components/Dashboard/DashboardControls.d.vue.ts +58 -0
- package/dist/runtime/components/Dashboard/DashboardControls.vue +209 -0
- package/dist/runtime/components/Dashboard/DashboardControls.vue.d.ts +58 -0
- package/dist/runtime/components/Dashboard/DashboardGrid.d.vue.ts +17 -0
- package/dist/runtime/components/Dashboard/DashboardGrid.vue +87 -0
- package/dist/runtime/components/Dashboard/DashboardGrid.vue.d.ts +17 -0
- package/dist/runtime/components/Dashboard/DashboardNameDialog.d.vue.ts +19 -0
- package/dist/runtime/components/Dashboard/DashboardNameDialog.vue +61 -0
- package/dist/runtime/components/Dashboard/DashboardNameDialog.vue.d.ts +19 -0
- package/dist/runtime/components/Dashboard/DashboardPickerDialog.d.vue.ts +15 -0
- package/dist/runtime/components/Dashboard/DashboardPickerDialog.vue +100 -0
- package/dist/runtime/components/Dashboard/DashboardPickerDialog.vue.d.ts +15 -0
- package/dist/runtime/components/Dashboard/DashboardShareDialog.d.vue.ts +28 -0
- package/dist/runtime/components/Dashboard/DashboardShareDialog.vue +155 -0
- package/dist/runtime/components/Dashboard/DashboardShareDialog.vue.d.ts +28 -0
- package/dist/runtime/components/Dashboard/DashboardTopBar.d.vue.ts +64 -0
- package/dist/runtime/components/Dashboard/DashboardTopBar.vue +79 -0
- package/dist/runtime/components/Dashboard/DashboardTopBar.vue.d.ts +64 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.d.vue.ts +44 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.vue +153 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetConfigDialog.vue.d.ts +44 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetFrame.d.vue.ts +28 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetFrame.vue +80 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetFrame.vue.d.ts +28 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetHost.d.vue.ts +7 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetHost.vue +30 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetHost.vue.d.ts +7 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetIcon.d.vue.ts +9 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetIcon.vue +13 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetIcon.vue.d.ts +9 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetLibrary.d.vue.ts +24 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetLibrary.vue +140 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetLibrary.vue.d.ts +24 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetPreview.d.vue.ts +23 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetPreview.vue +124 -0
- package/dist/runtime/components/Dashboard/DashboardWidgetPreview.vue.d.ts +23 -0
- package/dist/runtime/components/DataTable/ColumnsMenu.d.vue.ts +2 -1
- package/dist/runtime/components/DataTable/ColumnsMenu.vue.d.ts +2 -1
- package/dist/runtime/components/DataTable/DataTable.d.vue.ts +105 -28
- package/dist/runtime/components/DataTable/DataTable.vue +403 -38
- package/dist/runtime/components/DataTable/DataTable.vue.d.ts +105 -28
- package/dist/runtime/components/DataTable/FacetChips.d.vue.ts +17 -0
- package/dist/runtime/components/DataTable/FacetChips.vue +72 -0
- package/dist/runtime/components/DataTable/FacetChips.vue.d.ts +17 -0
- package/dist/runtime/components/DataTable/FacetPanel.d.vue.ts +21 -0
- package/dist/runtime/components/DataTable/FacetPanel.vue +359 -0
- package/dist/runtime/components/DataTable/FacetPanel.vue.d.ts +21 -0
- package/dist/runtime/components/DataTable/FilterMenu.d.vue.ts +6 -5
- package/dist/runtime/components/DataTable/FilterMenu.vue.d.ts +6 -5
- package/dist/runtime/components/DataTable/SavedViewsMenu.d.vue.ts +6 -1
- package/dist/runtime/components/DataTable/SavedViewsMenu.vue +83 -63
- package/dist/runtime/components/DataTable/SavedViewsMenu.vue.d.ts +6 -1
- package/dist/runtime/components/DataTable/SortMenu.d.vue.ts +9 -3
- package/dist/runtime/components/DataTable/SortMenu.vue +40 -12
- package/dist/runtime/components/DataTable/SortMenu.vue.d.ts +9 -3
- package/dist/runtime/components/DataTable/ViewModeSwitch.d.vue.ts +4 -2
- package/dist/runtime/components/DataTable/ViewModeSwitch.vue.d.ts +4 -2
- package/dist/runtime/components/Form/FieldCell.d.vue.ts +32 -0
- package/dist/runtime/components/Form/FieldCell.vue +46 -0
- package/dist/runtime/components/Form/FieldCell.vue.d.ts +32 -0
- package/dist/runtime/components/Form/FieldGrid.d.vue.ts +13 -0
- package/dist/runtime/components/Form/FieldGrid.vue +11 -0
- package/dist/runtime/components/Form/FieldGrid.vue.d.ts +13 -0
- package/dist/runtime/components/Form/FormContextCard.d.vue.ts +33 -0
- package/dist/runtime/components/Form/FormContextCard.vue +56 -0
- package/dist/runtime/components/Form/FormContextCard.vue.d.ts +33 -0
- package/dist/runtime/components/Form/FormLayout.d.vue.ts +47 -0
- package/dist/runtime/components/Form/FormLayout.vue +169 -0
- package/dist/runtime/components/Form/FormLayout.vue.d.ts +47 -0
- package/dist/runtime/components/Form/FormSection.d.vue.ts +25 -0
- package/dist/runtime/components/Form/FormSection.vue +48 -0
- package/dist/runtime/components/Form/FormSection.vue.d.ts +25 -0
- package/dist/runtime/components/Form/SaveBar.d.vue.ts +44 -0
- package/dist/runtime/components/Form/SaveBar.vue +71 -0
- package/dist/runtime/components/Form/SaveBar.vue.d.ts +44 -0
- package/dist/runtime/components/Media/MediaFolderTree.d.vue.ts +2 -1
- package/dist/runtime/components/Media/MediaFolderTree.vue.d.ts +2 -1
- package/dist/runtime/components/Media/MediaPickerDialog.d.vue.ts +4 -3
- package/dist/runtime/components/Media/MediaPickerDialog.vue.d.ts +4 -3
- package/dist/runtime/components/Media/MediaUploader.d.vue.ts +4 -3
- package/dist/runtime/components/Media/MediaUploader.vue.d.ts +4 -3
- package/dist/runtime/components/Shared/AppIcon.d.vue.ts +21 -0
- package/dist/runtime/components/Shared/AppIcon.vue +47 -0
- package/dist/runtime/components/Shared/AppIcon.vue.d.ts +21 -0
- package/dist/runtime/components/Shared/BuildLogsFlyout.d.vue.ts +2 -1
- package/dist/runtime/components/Shared/BuildLogsFlyout.vue.d.ts +2 -1
- package/dist/runtime/components/Shared/DetailSheet.d.vue.ts +60 -0
- package/dist/runtime/components/Shared/DetailSheet.vue +207 -0
- package/dist/runtime/components/Shared/DetailSheet.vue.d.ts +60 -0
- package/dist/runtime/components/Shared/FloatingPanel.d.vue.ts +7 -6
- package/dist/runtime/components/Shared/FloatingPanel.vue.d.ts +7 -6
- package/dist/runtime/components/Shared/GitDeploymentSettings.d.vue.ts +2 -1
- package/dist/runtime/components/Shared/GitDeploymentSettings.vue.d.ts +2 -1
- package/dist/runtime/components/Shared/KpiTile.d.vue.ts +2 -1
- package/dist/runtime/components/Shared/KpiTile.vue.d.ts +2 -1
- package/dist/runtime/components/Shared/ListSurface.d.vue.ts +3 -2
- package/dist/runtime/components/Shared/ListSurface.vue.d.ts +3 -2
- package/dist/runtime/components/Shared/PageHeader.d.vue.ts +7 -6
- package/dist/runtime/components/Shared/PageHeader.vue.d.ts +7 -6
- package/dist/runtime/components/Shared/RecordLayout.d.vue.ts +8 -7
- package/dist/runtime/components/Shared/RecordLayout.vue.d.ts +8 -7
- package/dist/runtime/components/Shared/RecordRailCard.d.vue.ts +3 -2
- package/dist/runtime/components/Shared/RecordRailCard.vue.d.ts +3 -2
- package/dist/runtime/components/Shared/SidebarNav.d.vue.ts +3 -2
- package/dist/runtime/components/Shared/SidebarNav.vue.d.ts +3 -2
- package/dist/runtime/components/Shared/SidebarNavLink.d.vue.ts +2 -1
- package/dist/runtime/components/Shared/SidebarNavLink.vue.d.ts +2 -1
- package/dist/runtime/components/Shared/SortHeader.d.vue.ts +3 -2
- package/dist/runtime/components/Shared/SortHeader.vue +6 -2
- package/dist/runtime/components/Shared/SortHeader.vue.d.ts +3 -2
- package/dist/runtime/components/Shared/StateCard.d.vue.ts +6 -5
- package/dist/runtime/components/Shared/StateCard.vue.d.ts +6 -5
- package/dist/runtime/components/Shared/StudioCard.d.vue.ts +5 -4
- package/dist/runtime/components/Shared/StudioCard.vue.d.ts +5 -4
- package/dist/runtime/components/Shared/TabBar.d.vue.ts +9 -10
- package/dist/runtime/components/Shared/TabBar.vue +26 -7
- package/dist/runtime/components/Shared/TabBar.vue.d.ts +9 -10
- package/dist/runtime/components/Shared/ViewTab.d.vue.ts +2 -1
- package/dist/runtime/components/Shared/ViewTab.vue.d.ts +2 -1
- package/dist/runtime/components/Shared/Wizard.d.vue.ts +10 -7
- package/dist/runtime/components/Shared/Wizard.vue.d.ts +10 -7
- package/dist/runtime/composables/useDashboards.d.ts +27 -0
- package/dist/runtime/composables/useDashboards.js +143 -0
- package/dist/runtime/composables/useDetailSheet.d.ts +20 -0
- package/dist/runtime/composables/useDetailSheet.js +50 -0
- package/dist/runtime/composables/usePlatformTenant.d.ts +6 -0
- package/dist/runtime/composables/useSavedViews.d.ts +44 -0
- package/dist/runtime/composables/useSavedViews.js +44 -0
- package/dist/runtime/composables/useStickyListView.d.ts +60 -0
- package/dist/runtime/composables/useStickyListView.js +32 -0
- package/dist/runtime/lib/activeTenant.d.ts +58 -0
- package/dist/runtime/lib/activeTenant.js +45 -0
- package/dist/runtime/lib/appIcon.d.ts +48 -0
- package/dist/runtime/lib/appIcon.js +27 -0
- package/dist/runtime/lib/canonicalJson.d.ts +43 -0
- package/dist/runtime/lib/canonicalJson.js +38 -0
- package/dist/runtime/lib/dashboardIcons.d.ts +4 -0
- package/dist/runtime/lib/dashboardIcons.js +50 -0
- package/dist/runtime/lib/detailSheet.d.ts +43 -0
- package/dist/runtime/lib/detailSheet.js +34 -0
- package/dist/runtime/lib/facets.d.ts +200 -0
- package/dist/runtime/lib/facets.js +171 -0
- package/dist/runtime/lib/formLayout.d.ts +94 -0
- package/dist/runtime/lib/formLayout.js +70 -0
- package/dist/runtime/lib/formLayoutContext.d.ts +26 -0
- package/dist/runtime/lib/formLayoutContext.js +1 -0
- package/dist/runtime/lib/gridTracks.d.ts +55 -0
- package/dist/runtime/lib/gridTracks.js +29 -0
- package/dist/runtime/lib/rowMove.d.ts +38 -0
- package/dist/runtime/lib/rowMove.js +20 -0
- package/dist/runtime/lib/solarIconMap.d.ts +15 -0
- package/dist/runtime/lib/solarIconMap.js +280 -0
- package/dist/runtime/lib/solarIcons.d.ts +37 -0
- package/dist/runtime/lib/solarIcons.js +25 -0
- package/dist/runtime/lib/widgetRegistry.d.ts +124 -0
- package/dist/runtime/lib/widgetRegistry.js +66 -0
- package/dist/runtime/types/dashboard.d.ts +95 -0
- package/dist/runtime/types/dashboard.js +2 -0
- package/dist/types.d.mts +12 -0
- package/package.json +3 -2
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label } from "@revenexx/studio";
|
|
3
|
+
const props = defineProps({
|
|
4
|
+
open: { type: Boolean, required: true },
|
|
5
|
+
title: { type: String, required: true },
|
|
6
|
+
description: { type: String, required: false },
|
|
7
|
+
label: { type: String, required: true },
|
|
8
|
+
placeholder: { type: String, required: false },
|
|
9
|
+
confirmLabel: { type: String, required: true },
|
|
10
|
+
initial: { type: String, required: false }
|
|
11
|
+
});
|
|
12
|
+
const emit = defineEmits(["update:open", "submit"]);
|
|
13
|
+
const t = useT();
|
|
14
|
+
const value = ref("");
|
|
15
|
+
const field = ref(null);
|
|
16
|
+
watch(() => props.open, async (open) => {
|
|
17
|
+
if (!open) return;
|
|
18
|
+
value.value = props.initial ?? "";
|
|
19
|
+
await nextTick();
|
|
20
|
+
const el = field.value?.$el ?? field.value;
|
|
21
|
+
el?.querySelector?.("input")?.focus?.() ?? el?.focus?.();
|
|
22
|
+
});
|
|
23
|
+
const canSubmit = computed(() => value.value.trim().length > 0);
|
|
24
|
+
function submit() {
|
|
25
|
+
if (!canSubmit.value) return;
|
|
26
|
+
emit("submit", value.value.trim());
|
|
27
|
+
emit("update:open", false);
|
|
28
|
+
}
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<Dialog :open="open" @update:open="emit('update:open', $event)">
|
|
33
|
+
<DialogContent class="max-w-md">
|
|
34
|
+
<DialogHeader>
|
|
35
|
+
<DialogTitle>{{ title }}</DialogTitle>
|
|
36
|
+
<DialogDescription v-if="description">{{ description }}</DialogDescription>
|
|
37
|
+
</DialogHeader>
|
|
38
|
+
|
|
39
|
+
<div class="space-y-1.5">
|
|
40
|
+
<Label for="dashboard-name">{{ label }}</Label>
|
|
41
|
+
<Input
|
|
42
|
+
id="dashboard-name"
|
|
43
|
+
ref="field"
|
|
44
|
+
v-model="value"
|
|
45
|
+
class="bg-white"
|
|
46
|
+
:placeholder="placeholder"
|
|
47
|
+
@keydown.enter.prevent="submit"
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<DialogFooter>
|
|
52
|
+
<Button variant="outline" size="sm" class="h-7 text-sm" @click="emit('update:open', false)">
|
|
53
|
+
{{ t("dashboard.dialog.action.cancel") }}
|
|
54
|
+
</Button>
|
|
55
|
+
<Button size="sm" class="h-7 text-sm" :disabled="!canSubmit" @click="submit">
|
|
56
|
+
{{ confirmLabel }}
|
|
57
|
+
</Button>
|
|
58
|
+
</DialogFooter>
|
|
59
|
+
</DialogContent>
|
|
60
|
+
</Dialog>
|
|
61
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
open: boolean;
|
|
3
|
+
title: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
label: string;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
confirmLabel: string;
|
|
8
|
+
/** Pre-filled value — a rename starts from the current name. */
|
|
9
|
+
initial?: string;
|
|
10
|
+
};
|
|
11
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
12
|
+
"update:open": (value: boolean) => any;
|
|
13
|
+
submit: (name: string) => any;
|
|
14
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
15
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
16
|
+
onSubmit?: ((name: string) => any) | undefined;
|
|
17
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
declare const _default: typeof __VLS_export;
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Dashboard } from '../../types/dashboard.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
dashboards: Dashboard[];
|
|
5
|
+
currentId?: string | null;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
8
|
+
"update:open": (value: boolean) => any;
|
|
9
|
+
select: (id: string) => any;
|
|
10
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
11
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
12
|
+
onSelect?: ((id: string) => any) | undefined;
|
|
13
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, Input } from "@revenexx/studio";
|
|
3
|
+
import { Global, Lock, Magnifier, Pen, Star } from "#solar-icons";
|
|
4
|
+
import { computed, ref } from "vue";
|
|
5
|
+
import { dateTimeFormat } from "../../lib/formatBridge";
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
open: { type: Boolean, required: true },
|
|
8
|
+
dashboards: { type: Array, required: true },
|
|
9
|
+
currentId: { type: [String, null], required: false }
|
|
10
|
+
});
|
|
11
|
+
const emit = defineEmits(["update:open", "select"]);
|
|
12
|
+
const t = useT();
|
|
13
|
+
function formatDateTime(value) {
|
|
14
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
15
|
+
return Number.isNaN(date.getTime()) ? "" : dateTimeFormat({ dateStyle: "medium", timeStyle: "short" }).format(date);
|
|
16
|
+
}
|
|
17
|
+
const query = ref("");
|
|
18
|
+
watch(() => props.open, (open) => {
|
|
19
|
+
if (open) query.value = "";
|
|
20
|
+
});
|
|
21
|
+
const groups = computed(() => {
|
|
22
|
+
const term = query.value.trim().toLowerCase();
|
|
23
|
+
const match = (d) => !term || d.name.toLowerCase().includes(term) || (d.ownerName ?? "").toLowerCase().includes(term);
|
|
24
|
+
return [
|
|
25
|
+
{ key: "mine", label: t("dashboard.header.group.mine"), items: props.dashboards.filter((d) => d.isOwner && match(d)) },
|
|
26
|
+
{ key: "shared", label: t("dashboard.header.group.shared"), items: props.dashboards.filter((d) => !d.isOwner && match(d)) }
|
|
27
|
+
].filter((group) => group.items.length > 0);
|
|
28
|
+
});
|
|
29
|
+
const total = computed(() => groups.value.reduce((sum, g) => sum + g.items.length, 0));
|
|
30
|
+
function pick(id) {
|
|
31
|
+
emit("select", id);
|
|
32
|
+
emit("update:open", false);
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template>
|
|
37
|
+
<Dialog :open="open" @update:open="emit('update:open', $event)">
|
|
38
|
+
<DialogContent class="max-w-2xl">
|
|
39
|
+
<DialogHeader>
|
|
40
|
+
<DialogTitle>{{ t("dashboard.picker.title") }}</DialogTitle>
|
|
41
|
+
<DialogDescription>{{ t("dashboard.picker.description", { count: dashboards.length }) }}</DialogDescription>
|
|
42
|
+
</DialogHeader>
|
|
43
|
+
|
|
44
|
+
<div class="relative">
|
|
45
|
+
<Magnifier :size="14" class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
|
|
46
|
+
<Input v-model="query" :placeholder="t('dashboard.picker.search_placeholder')" class="bg-white pl-8" />
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div class="max-h-[55vh] -mx-1 space-y-4 overflow-y-auto px-1 pb-1">
|
|
50
|
+
<p v-if="!total" class="py-10 text-center text-sm text-muted-foreground">
|
|
51
|
+
{{ t("dashboard.picker.no_matches", { query }) }}
|
|
52
|
+
</p>
|
|
53
|
+
|
|
54
|
+
<section v-for="group in groups" :key="group.key">
|
|
55
|
+
<h4 class="mb-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
56
|
+
{{ group.label }}
|
|
57
|
+
</h4>
|
|
58
|
+
<ul class="flex flex-col gap-1">
|
|
59
|
+
<li v-for="dashboard in group.items" :key="dashboard.id">
|
|
60
|
+
<button
|
|
61
|
+
type="button"
|
|
62
|
+
class="flex w-full items-center gap-3 rounded-md border px-3 py-2 text-left transition"
|
|
63
|
+
:class="dashboard.id === currentId ? 'border-primary/40 bg-primary/[0.04]' : 'border-transparent hover:border-black/[0.08] hover:bg-black/[0.03]'"
|
|
64
|
+
@click="pick(dashboard.id)"
|
|
65
|
+
>
|
|
66
|
+
<span class="min-w-0 flex-1">
|
|
67
|
+
<span class="flex items-center gap-1.5">
|
|
68
|
+
<span class="truncate text-sm font-medium text-[#0B0522]">{{ dashboard.name }}</span>
|
|
69
|
+
<span
|
|
70
|
+
v-if="dashboard.isDefault"
|
|
71
|
+
class="shrink-0 rounded bg-amber-50 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-amber-700"
|
|
72
|
+
>{{ t("dashboard.header.menu.default_badge") }}</span>
|
|
73
|
+
</span>
|
|
74
|
+
<span class="mt-0.5 block truncate text-xs text-muted-foreground">
|
|
75
|
+
<template v-if="dashboard.ownerName">{{ t("dashboard.picker.owner", { name: dashboard.ownerName }) }} · </template>
|
|
76
|
+
{{ t("dashboard.picker.updated", { at: formatDateTime(dashboard.updatedAt) }) }}
|
|
77
|
+
</span>
|
|
78
|
+
</span>
|
|
79
|
+
|
|
80
|
+
<!-- How it is shared, at a glance: private, read-only or open. -->
|
|
81
|
+
<span class="flex shrink-0 items-center gap-1 text-xs text-muted-foreground">
|
|
82
|
+
<template v-if="dashboard.visibility === 'private'">
|
|
83
|
+
<Lock :size="12" /> {{ t("dashboard.share.private.title") }}
|
|
84
|
+
</template>
|
|
85
|
+
<template v-else-if="dashboard.tenantPermission === 'edit'">
|
|
86
|
+
<Pen :size="12" /> {{ t("dashboard.share.permission.edit.title") }}
|
|
87
|
+
</template>
|
|
88
|
+
<template v-else>
|
|
89
|
+
<Global :size="12" /> {{ t("dashboard.share.permission.view.title") }}
|
|
90
|
+
</template>
|
|
91
|
+
</span>
|
|
92
|
+
<Star v-if="dashboard.id === currentId" :size="12" class="shrink-0 text-primary opacity-0" />
|
|
93
|
+
</button>
|
|
94
|
+
</li>
|
|
95
|
+
</ul>
|
|
96
|
+
</section>
|
|
97
|
+
</div>
|
|
98
|
+
</DialogContent>
|
|
99
|
+
</Dialog>
|
|
100
|
+
</template>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Dashboard } from '../../types/dashboard.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
dashboards: Dashboard[];
|
|
5
|
+
currentId?: string | null;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
8
|
+
"update:open": (value: boolean) => any;
|
|
9
|
+
select: (id: string) => any;
|
|
10
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
11
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
12
|
+
onSelect?: ((id: string) => any) | undefined;
|
|
13
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Dashboard, DashboardTenantPermission, DashboardVisibility } from '../../types/dashboard.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
dashboard: Dashboard | null;
|
|
5
|
+
/**
|
|
6
|
+
* Where this surface's dashboards live. The link is `path?dashboard=slug`,
|
|
7
|
+
* and a Commerce dashboard opened at `/` would land on the home screen and
|
|
8
|
+
* silently resolve to something else.
|
|
9
|
+
*/
|
|
10
|
+
path?: string;
|
|
11
|
+
};
|
|
12
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
13
|
+
"update:open": (value: boolean) => any;
|
|
14
|
+
change: (payload: {
|
|
15
|
+
visibility: DashboardVisibility;
|
|
16
|
+
tenantPermission: DashboardTenantPermission;
|
|
17
|
+
}) => any;
|
|
18
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
19
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
20
|
+
onChange?: ((payload: {
|
|
21
|
+
visibility: DashboardVisibility;
|
|
22
|
+
tenantPermission: DashboardTenantPermission;
|
|
23
|
+
}) => any) | undefined;
|
|
24
|
+
}>, {
|
|
25
|
+
path: string;
|
|
26
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@revenexx/studio";
|
|
3
|
+
import { CheckCircle, Copy, Global, Lock, Pen } from "#solar-icons";
|
|
4
|
+
import { computed, ref, watch } from "vue";
|
|
5
|
+
import { dateTimeFormat } from "../../lib/formatBridge";
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
open: { type: Boolean, required: true },
|
|
8
|
+
dashboard: { type: [Object, null], required: true },
|
|
9
|
+
path: { type: String, required: false, default: "/" }
|
|
10
|
+
});
|
|
11
|
+
const emit = defineEmits(["update:open", "change"]);
|
|
12
|
+
const t = useT();
|
|
13
|
+
const router = useRouter();
|
|
14
|
+
function modeOf(dashboard) {
|
|
15
|
+
if (!dashboard || dashboard.visibility !== "tenant") return "private";
|
|
16
|
+
return dashboard.tenantPermission === "edit" ? "tenant-edit" : "tenant-view";
|
|
17
|
+
}
|
|
18
|
+
const MODE_VALUES = {
|
|
19
|
+
"private": { visibility: "private", tenantPermission: "view" },
|
|
20
|
+
"tenant-view": { visibility: "tenant", tenantPermission: "view" },
|
|
21
|
+
"tenant-edit": { visibility: "tenant", tenantPermission: "edit" }
|
|
22
|
+
};
|
|
23
|
+
const choice = ref("private");
|
|
24
|
+
watch(() => props.open, (open) => {
|
|
25
|
+
if (open) choice.value = modeOf(props.dashboard);
|
|
26
|
+
});
|
|
27
|
+
function formatDateTime(value) {
|
|
28
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
29
|
+
return Number.isNaN(date.getTime()) ? "" : dateTimeFormat({ dateStyle: "medium", timeStyle: "short" }).format(date);
|
|
30
|
+
}
|
|
31
|
+
const options = computed(() => [
|
|
32
|
+
{
|
|
33
|
+
value: "private",
|
|
34
|
+
icon: Lock,
|
|
35
|
+
title: t("dashboard.share.private.title"),
|
|
36
|
+
description: t("dashboard.share.private.description")
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
value: "tenant-view",
|
|
40
|
+
icon: Global,
|
|
41
|
+
title: t("dashboard.share.mode.view.title"),
|
|
42
|
+
description: t("dashboard.share.mode.view.description")
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
value: "tenant-edit",
|
|
46
|
+
icon: Pen,
|
|
47
|
+
title: t("dashboard.share.mode.edit.title"),
|
|
48
|
+
description: t("dashboard.share.mode.edit.description")
|
|
49
|
+
}
|
|
50
|
+
]);
|
|
51
|
+
const link = computed(() => {
|
|
52
|
+
if (!props.dashboard || !import.meta.client) return "";
|
|
53
|
+
const href = router.resolve({ path: props.path, query: { dashboard: props.dashboard.slug } }).href;
|
|
54
|
+
return new URL(href, window.location.origin).toString();
|
|
55
|
+
});
|
|
56
|
+
const copied = ref(false);
|
|
57
|
+
let copiedTimer = null;
|
|
58
|
+
async function copyLink() {
|
|
59
|
+
try {
|
|
60
|
+
await navigator.clipboard.writeText(link.value);
|
|
61
|
+
copied.value = true;
|
|
62
|
+
if (copiedTimer) clearTimeout(copiedTimer);
|
|
63
|
+
copiedTimer = setTimeout(() => {
|
|
64
|
+
copied.value = false;
|
|
65
|
+
}, 2e3);
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
onUnmounted(() => {
|
|
70
|
+
if (copiedTimer) clearTimeout(copiedTimer);
|
|
71
|
+
});
|
|
72
|
+
const meta = computed(() => {
|
|
73
|
+
const d = props.dashboard;
|
|
74
|
+
if (!d) return "";
|
|
75
|
+
const parts = [];
|
|
76
|
+
if (d.ownerName) parts.push(t("dashboard.share.meta.created", { name: d.ownerName }));
|
|
77
|
+
parts.push(d.updatedByName ? t("dashboard.share.meta.updated", { name: d.updatedByName, at: formatDateTime(d.updatedAt) }) : t("dashboard.share.meta.updated_unknown", { at: formatDateTime(d.updatedAt) }));
|
|
78
|
+
if (!parts[0].match(/^[A-ZÄÖÜ]/)) parts[0] = parts[0].charAt(0).toUpperCase() + parts[0].slice(1);
|
|
79
|
+
return parts.join(" \xB7 ");
|
|
80
|
+
});
|
|
81
|
+
const dirty = computed(() => choice.value !== modeOf(props.dashboard));
|
|
82
|
+
function apply() {
|
|
83
|
+
if (dirty.value) emit("change", MODE_VALUES[choice.value]);
|
|
84
|
+
emit("update:open", false);
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<Dialog :open="open" @update:open="emit('update:open', $event)">
|
|
90
|
+
<DialogContent class="max-w-lg">
|
|
91
|
+
<DialogHeader>
|
|
92
|
+
<DialogTitle>{{ t("dashboard.share.title", { name: dashboard?.name ?? "" }) }}</DialogTitle>
|
|
93
|
+
<DialogDescription>{{ t("dashboard.share.description") }}</DialogDescription>
|
|
94
|
+
</DialogHeader>
|
|
95
|
+
|
|
96
|
+
<div class="space-y-2">
|
|
97
|
+
<button
|
|
98
|
+
v-for="option in options"
|
|
99
|
+
:key="option.value"
|
|
100
|
+
type="button"
|
|
101
|
+
class="flex w-full items-start gap-3 rounded-lg border p-3 text-left transition"
|
|
102
|
+
:class="choice === option.value ? 'border-primary/40 bg-primary/[0.04]' : 'border-black/[0.06] hover:border-black/15 hover:bg-black/[0.02]'"
|
|
103
|
+
@click="choice = option.value"
|
|
104
|
+
>
|
|
105
|
+
<span
|
|
106
|
+
class="mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-md"
|
|
107
|
+
:class="choice === option.value ? 'bg-primary/10 text-primary' : 'bg-black/[0.04] text-muted-foreground'"
|
|
108
|
+
>
|
|
109
|
+
<component :is="option.icon" :size="15" />
|
|
110
|
+
</span>
|
|
111
|
+
<span class="min-w-0 flex-1">
|
|
112
|
+
<span class="block text-sm font-medium text-[#0B0522]">{{ option.title }}</span>
|
|
113
|
+
<span class="mt-0.5 block text-xs leading-snug text-muted-foreground">{{ option.description }}</span>
|
|
114
|
+
</span>
|
|
115
|
+
<CheckCircle v-if="choice === option.value" :size="16" class="mt-0.5 shrink-0 text-primary" />
|
|
116
|
+
</button>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<!-- The link works for anyone who can already see the dashboard; it is a
|
|
120
|
+
convenience, not a grant. Sharing is what grants. -->
|
|
121
|
+
<div v-if="choice !== 'private'" class="flex items-center gap-2 rounded-md border border-black/[0.06] bg-black/[0.02] px-2 py-1.5">
|
|
122
|
+
<input
|
|
123
|
+
:value="link"
|
|
124
|
+
readonly
|
|
125
|
+
class="min-w-0 flex-1 truncate bg-transparent text-xs text-muted-foreground outline-none"
|
|
126
|
+
@focus="$event.target.select()"
|
|
127
|
+
>
|
|
128
|
+
<button
|
|
129
|
+
type="button"
|
|
130
|
+
class="flex shrink-0 items-center gap-1 rounded px-1.5 py-1 text-xs font-medium text-primary hover:bg-primary/10"
|
|
131
|
+
@click="copyLink"
|
|
132
|
+
>
|
|
133
|
+
<component :is="copied ? CheckCircle : Copy" :size="13" />
|
|
134
|
+
{{ copied ? t("dashboard.share.link.copied") : t("dashboard.share.link.copy") }}
|
|
135
|
+
</button>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<!-- Who made it and who touched it last. Names are captured at write
|
|
139
|
+
time; the cockpit has no user directory to resolve a subject id
|
|
140
|
+
against, and a lookup per row would not be worth it. -->
|
|
141
|
+
<p v-if="meta" class="border-t border-black/[0.06] pt-3 text-xs text-muted-foreground">
|
|
142
|
+
{{ meta }}
|
|
143
|
+
</p>
|
|
144
|
+
|
|
145
|
+
<DialogFooter>
|
|
146
|
+
<Button variant="outline" size="sm" class="h-7 text-sm" @click="emit('update:open', false)">
|
|
147
|
+
{{ t("dashboard.dialog.action.cancel") }}
|
|
148
|
+
</Button>
|
|
149
|
+
<Button size="sm" class="h-7 text-sm" :disabled="!dirty" @click="apply">
|
|
150
|
+
{{ t("dashboard.share.action") }}
|
|
151
|
+
</Button>
|
|
152
|
+
</DialogFooter>
|
|
153
|
+
</DialogContent>
|
|
154
|
+
</Dialog>
|
|
155
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Dashboard, DashboardTenantPermission, DashboardVisibility } from '../../types/dashboard.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
dashboard: Dashboard | null;
|
|
5
|
+
/**
|
|
6
|
+
* Where this surface's dashboards live. The link is `path?dashboard=slug`,
|
|
7
|
+
* and a Commerce dashboard opened at `/` would land on the home screen and
|
|
8
|
+
* silently resolve to something else.
|
|
9
|
+
*/
|
|
10
|
+
path?: string;
|
|
11
|
+
};
|
|
12
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
13
|
+
"update:open": (value: boolean) => any;
|
|
14
|
+
change: (payload: {
|
|
15
|
+
visibility: DashboardVisibility;
|
|
16
|
+
tenantPermission: DashboardTenantPermission;
|
|
17
|
+
}) => any;
|
|
18
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
19
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
20
|
+
onChange?: ((payload: {
|
|
21
|
+
visibility: DashboardVisibility;
|
|
22
|
+
tenantPermission: DashboardTenantPermission;
|
|
23
|
+
}) => any) | undefined;
|
|
24
|
+
}>, {
|
|
25
|
+
path: string;
|
|
26
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Dashboard } from '../../types/dashboard.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
dashboards: Dashboard[];
|
|
4
|
+
current: Dashboard | null;
|
|
5
|
+
editing: boolean;
|
|
6
|
+
dirty: boolean;
|
|
7
|
+
saving: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* The person this row greets. A prop rather than a lookup: who is signed in
|
|
10
|
+
* is the HOST's question, and a published module has no user directory.
|
|
11
|
+
*/
|
|
12
|
+
userName?: string;
|
|
13
|
+
/** The line above the greeting. Defaults to today's date. */
|
|
14
|
+
eyebrow?: string;
|
|
15
|
+
/** The big line. Defaults to the greeting. */
|
|
16
|
+
heading?: string;
|
|
17
|
+
};
|
|
18
|
+
declare var __VLS_21: {
|
|
19
|
+
pillClass: "group cursor-pointer rounded-lg border border-gray-400/10 outline-1 outline-white/30 transition-colors hover:border-primary/50 disabled:cursor-not-allowed disabled:opacity-55 disabled:hover:border-gray-400/10";
|
|
20
|
+
paneClass: "flex items-center gap-1.5 rounded-lg bg-white px-2.5 py-1.5 text-sm font-medium text-[#0B0522] transition-colors group-hover:bg-muted/60 group-disabled:group-hover:bg-white";
|
|
21
|
+
compact: boolean;
|
|
22
|
+
};
|
|
23
|
+
type __VLS_Slots = {} & {
|
|
24
|
+
scope?: (props: typeof __VLS_21) => any;
|
|
25
|
+
};
|
|
26
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
27
|
+
edit: () => any;
|
|
28
|
+
select: (id: string) => any;
|
|
29
|
+
save: () => any;
|
|
30
|
+
discard: () => any;
|
|
31
|
+
addWidget: () => any;
|
|
32
|
+
rename: () => any;
|
|
33
|
+
share: () => any;
|
|
34
|
+
browse: () => any;
|
|
35
|
+
create: () => any;
|
|
36
|
+
duplicate: () => any;
|
|
37
|
+
remove: () => any;
|
|
38
|
+
setDefault: () => any;
|
|
39
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
40
|
+
onEdit?: (() => any) | undefined;
|
|
41
|
+
onSelect?: ((id: string) => any) | undefined;
|
|
42
|
+
onSave?: (() => any) | undefined;
|
|
43
|
+
onDiscard?: (() => any) | undefined;
|
|
44
|
+
onAddWidget?: (() => any) | undefined;
|
|
45
|
+
onRename?: (() => any) | undefined;
|
|
46
|
+
onShare?: (() => any) | undefined;
|
|
47
|
+
onBrowse?: (() => any) | undefined;
|
|
48
|
+
onCreate?: (() => any) | undefined;
|
|
49
|
+
onDuplicate?: (() => any) | undefined;
|
|
50
|
+
onRemove?: (() => any) | undefined;
|
|
51
|
+
onSetDefault?: (() => any) | undefined;
|
|
52
|
+
}>, {
|
|
53
|
+
heading: string;
|
|
54
|
+
userName: string;
|
|
55
|
+
eyebrow: string;
|
|
56
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
57
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
58
|
+
declare const _default: typeof __VLS_export;
|
|
59
|
+
export default _default;
|
|
60
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
61
|
+
new (): {
|
|
62
|
+
$slots: S;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import DashboardControls from "./DashboardControls.vue";
|
|
4
|
+
import { dateTimeFormat } from "../../lib/formatBridge";
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
dashboards: { type: Array, required: true },
|
|
7
|
+
current: { type: [Object, null], required: true },
|
|
8
|
+
editing: { type: Boolean, required: true },
|
|
9
|
+
dirty: { type: Boolean, required: true },
|
|
10
|
+
saving: { type: Boolean, required: true },
|
|
11
|
+
userName: { type: String, required: false, default: "" },
|
|
12
|
+
eyebrow: { type: String, required: false, default: "" },
|
|
13
|
+
heading: { type: String, required: false, default: "" }
|
|
14
|
+
});
|
|
15
|
+
const emit = defineEmits(["select", "edit", "save", "discard", "addWidget", "rename", "share", "browse", "create", "duplicate", "remove", "setDefault"]);
|
|
16
|
+
const t = useT();
|
|
17
|
+
const greeting = computed(() => {
|
|
18
|
+
const hour = (/* @__PURE__ */ new Date()).getHours();
|
|
19
|
+
if (hour < 11) return t("dashboard.topbar.greeting.morning");
|
|
20
|
+
if (hour < 17) return t("dashboard.topbar.greeting.day");
|
|
21
|
+
return t("dashboard.topbar.greeting.evening");
|
|
22
|
+
});
|
|
23
|
+
const fullName = computed(() => props.userName.trim());
|
|
24
|
+
const dateLabel = computed(() => props.eyebrow || dateTimeFormat({ weekday: "long", day: "numeric", month: "long" }).format(/* @__PURE__ */ new Date()));
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<div class="relative z-30 mb-3 rounded-lg border border-gray-400/10 outline-1 outline-white/30">
|
|
29
|
+
<div class="relative flex flex-wrap items-center justify-between gap-4 rounded-lg bg-gradient-to-br from-white via-violet-50/50 to-white px-5 py-5">
|
|
30
|
+
<!-- Decoration, in its own clipped layer. Clipping the row itself would
|
|
31
|
+
cut off the dashboard menu when it opens. Hidden from assistive tech,
|
|
32
|
+
and the drift stops for anyone who asked the system for less motion. -->
|
|
33
|
+
<div class="pointer-events-none absolute inset-0 overflow-hidden rounded-lg" aria-hidden="true">
|
|
34
|
+
<div class="absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/60 to-transparent motion-safe:animate-[dashboard-shimmer_9s_ease-in-out_infinite]" />
|
|
35
|
+
<div class="absolute -right-16 -top-20 size-56 rounded-full bg-gradient-to-br from-violet-300/25 to-amber-200/20 blur-3xl" />
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="relative min-w-0">
|
|
39
|
+
<div class="text-mono uppercase text-muted-foreground">
|
|
40
|
+
{{ dateLabel }}
|
|
41
|
+
</div>
|
|
42
|
+
<h1 class="mt-1 truncate text-2xl font-semibold text-[#0B0522]">
|
|
43
|
+
<template v-if="heading">{{ heading }}</template>
|
|
44
|
+
<template v-else>
|
|
45
|
+
{{ greeting }}<template v-if="fullName">, <span class="text-primary">{{ fullName }}</span></template>.
|
|
46
|
+
</template>
|
|
47
|
+
</h1>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<DashboardControls
|
|
51
|
+
:dashboards="dashboards"
|
|
52
|
+
:current="current"
|
|
53
|
+
:editing="editing"
|
|
54
|
+
:dirty="dirty"
|
|
55
|
+
:saving="saving"
|
|
56
|
+
@select="emit('select', $event)"
|
|
57
|
+
@edit="emit('edit')"
|
|
58
|
+
@save="emit('save')"
|
|
59
|
+
@discard="emit('discard')"
|
|
60
|
+
@add-widget="emit('addWidget')"
|
|
61
|
+
@rename="emit('rename')"
|
|
62
|
+
@share="emit('share')"
|
|
63
|
+
@browse="emit('browse')"
|
|
64
|
+
@create="emit('create')"
|
|
65
|
+
@duplicate="emit('duplicate')"
|
|
66
|
+
@remove="emit('remove')"
|
|
67
|
+
@set-default="emit('setDefault')"
|
|
68
|
+
>
|
|
69
|
+
<template #scope="slotProps">
|
|
70
|
+
<slot name="scope" v-bind="slotProps" />
|
|
71
|
+
</template>
|
|
72
|
+
</DashboardControls>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</template>
|
|
76
|
+
|
|
77
|
+
<style>
|
|
78
|
+
@keyframes dashboard-shimmer{0%,60%{transform:translateX(-100%)}to{transform:translateX(200%)}}
|
|
79
|
+
</style>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Dashboard } from '../../types/dashboard.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
dashboards: Dashboard[];
|
|
4
|
+
current: Dashboard | null;
|
|
5
|
+
editing: boolean;
|
|
6
|
+
dirty: boolean;
|
|
7
|
+
saving: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* The person this row greets. A prop rather than a lookup: who is signed in
|
|
10
|
+
* is the HOST's question, and a published module has no user directory.
|
|
11
|
+
*/
|
|
12
|
+
userName?: string;
|
|
13
|
+
/** The line above the greeting. Defaults to today's date. */
|
|
14
|
+
eyebrow?: string;
|
|
15
|
+
/** The big line. Defaults to the greeting. */
|
|
16
|
+
heading?: string;
|
|
17
|
+
};
|
|
18
|
+
declare var __VLS_21: {
|
|
19
|
+
pillClass: "group cursor-pointer rounded-lg border border-gray-400/10 outline-1 outline-white/30 transition-colors hover:border-primary/50 disabled:cursor-not-allowed disabled:opacity-55 disabled:hover:border-gray-400/10";
|
|
20
|
+
paneClass: "flex items-center gap-1.5 rounded-lg bg-white px-2.5 py-1.5 text-sm font-medium text-[#0B0522] transition-colors group-hover:bg-muted/60 group-disabled:group-hover:bg-white";
|
|
21
|
+
compact: boolean;
|
|
22
|
+
};
|
|
23
|
+
type __VLS_Slots = {} & {
|
|
24
|
+
scope?: (props: typeof __VLS_21) => any;
|
|
25
|
+
};
|
|
26
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
27
|
+
edit: () => any;
|
|
28
|
+
select: (id: string) => any;
|
|
29
|
+
save: () => any;
|
|
30
|
+
discard: () => any;
|
|
31
|
+
addWidget: () => any;
|
|
32
|
+
rename: () => any;
|
|
33
|
+
share: () => any;
|
|
34
|
+
browse: () => any;
|
|
35
|
+
create: () => any;
|
|
36
|
+
duplicate: () => any;
|
|
37
|
+
remove: () => any;
|
|
38
|
+
setDefault: () => any;
|
|
39
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
40
|
+
onEdit?: (() => any) | undefined;
|
|
41
|
+
onSelect?: ((id: string) => any) | undefined;
|
|
42
|
+
onSave?: (() => any) | undefined;
|
|
43
|
+
onDiscard?: (() => any) | undefined;
|
|
44
|
+
onAddWidget?: (() => any) | undefined;
|
|
45
|
+
onRename?: (() => any) | undefined;
|
|
46
|
+
onShare?: (() => any) | undefined;
|
|
47
|
+
onBrowse?: (() => any) | undefined;
|
|
48
|
+
onCreate?: (() => any) | undefined;
|
|
49
|
+
onDuplicate?: (() => any) | undefined;
|
|
50
|
+
onRemove?: (() => any) | undefined;
|
|
51
|
+
onSetDefault?: (() => any) | undefined;
|
|
52
|
+
}>, {
|
|
53
|
+
heading: string;
|
|
54
|
+
userName: string;
|
|
55
|
+
eyebrow: string;
|
|
56
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
57
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
58
|
+
declare const _default: typeof __VLS_export;
|
|
59
|
+
export default _default;
|
|
60
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
61
|
+
new (): {
|
|
62
|
+
$slots: S;
|
|
63
|
+
};
|
|
64
|
+
};
|