@chelone/vue 0.9.0 → 0.10.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/components/admin/layout/data-provider-devtools.vue_vue_type_script_setup_true_lang.js +61 -27
- package/dist/components/admin/layout/icons.d.ts +2 -0
- package/dist/components/admin/layout/icons.js +3 -1
- package/dist/components/admin/realtime/menu-live.vue.d.ts +3 -1
- package/dist/components/admin/realtime/menu-live.vue_vue_type_script_setup_true_lang.js +42 -12
- package/package.json +5 -5
package/dist/components/admin/layout/data-provider-devtools.vue_vue_type_script_setup_true_lang.js
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
import { useTranslate } from "../../../i18n/use-translate.js";
|
|
2
2
|
import { cn } from "../../../lib/utils.js";
|
|
3
3
|
import button_default from "../../ui/button.js";
|
|
4
|
+
import { buttonVariants } from "../../ui/button-variants.js";
|
|
4
5
|
import data_provider_devtools_entry_default from "./data-provider-devtools-entry.js";
|
|
5
|
-
import {
|
|
6
|
+
import { ActivityIcon } from "./icons.js";
|
|
7
|
+
import { Fragment, computed, createBlock, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, nextTick, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderList, shallowRef, toDisplayString, unref, watch, withCtx } from "vue";
|
|
6
8
|
import { shouldHandleHotkey } from "@chelone/core";
|
|
7
9
|
// src/components/admin/layout/data-provider-devtools.vue?vue&type=script&setup=true&lang.ts
|
|
8
|
-
var _hoisted_1 = ["aria-label"
|
|
9
|
-
var _hoisted_2 =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
var _hoisted_1 = ["aria-label"];
|
|
11
|
+
var _hoisted_2 = {
|
|
12
|
+
"data-slot": "data-provider-devtools-badge",
|
|
13
|
+
class: "absolute top-0 right-0 min-w-4 -translate-y-1/2 translate-x-1/2 rounded-full bg-primary px-1 text-center text-[10px] leading-4 text-primary-foreground tabular-nums"
|
|
14
|
+
};
|
|
15
|
+
var _hoisted_3 = ["aria-label"];
|
|
16
|
+
var _hoisted_4 = { class: "flex items-center gap-2 border-b px-3 py-1.5" };
|
|
17
|
+
var _hoisted_5 = { class: "font-semibold" };
|
|
18
|
+
var _hoisted_6 = { class: "text-muted-foreground tabular-nums" };
|
|
19
|
+
var _hoisted_7 = {
|
|
13
20
|
key: 0,
|
|
14
21
|
class: "px-3 py-2 text-muted-foreground"
|
|
15
22
|
};
|
|
16
|
-
var
|
|
23
|
+
var _hoisted_8 = {
|
|
17
24
|
key: 1,
|
|
18
25
|
class: "min-h-0 flex-1 overflow-y-auto"
|
|
19
26
|
};
|
|
@@ -30,11 +37,13 @@ var data_provider_devtools_vue_vue_type_script_setup_true_lang_default = /*@__PU
|
|
|
30
37
|
},
|
|
31
38
|
setup(__props) {
|
|
32
39
|
/**
|
|
33
|
-
* `<DataProviderDevtools>` — a
|
|
34
|
-
* the calls a `createDataProviderLog` log has recorded
|
|
35
|
-
*
|
|
40
|
+
* `<DataProviderDevtools>` — a round button at the bottom right carrying a
|
|
41
|
+
* badge with the number of calls a `createDataProviderLog` log has recorded.
|
|
42
|
+
* Activating it opens a panel listing those calls: method, resource, duration
|
|
43
|
+
* and, for a failed call, the error. The panel's Hide button, or the
|
|
44
|
+
* `shortcut`, closes it back to the button. The Vue twin of
|
|
36
45
|
* `@chelone/react`'s `layout/data-provider-devtools.tsx`. Wrap the data
|
|
37
|
-
* provider with `log.wrap`, then mount the
|
|
46
|
+
* provider with `log.wrap`, then mount the component once, in the layout.
|
|
38
47
|
*/
|
|
39
48
|
const props = __props;
|
|
40
49
|
const translate = useTranslate();
|
|
@@ -48,6 +57,7 @@ var data_provider_devtools_vue_vue_type_script_setup_true_lang_default = /*@__PU
|
|
|
48
57
|
}));
|
|
49
58
|
}, { immediate: true });
|
|
50
59
|
const entries = computed(() => [...snapshot.value.entries].reverse());
|
|
60
|
+
const title = () => translate("chelone.devtools.title", { _: "Data provider" });
|
|
51
61
|
function onKeydown(event) {
|
|
52
62
|
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
53
63
|
if (!shouldHandleHotkey(event, props.shortcut, target)) return;
|
|
@@ -56,24 +66,48 @@ var data_provider_devtools_vue_vue_type_script_setup_true_lang_default = /*@__PU
|
|
|
56
66
|
}
|
|
57
67
|
onMounted(() => window.addEventListener("keydown", onKeydown));
|
|
58
68
|
onBeforeUnmount(() => window.removeEventListener("keydown", onKeydown));
|
|
69
|
+
const triggerRef = ref(null);
|
|
70
|
+
const panelRef = ref(null);
|
|
71
|
+
watch(open, (opened) => {
|
|
72
|
+
nextTick(() => {
|
|
73
|
+
const focused = document.activeElement;
|
|
74
|
+
if (focused !== null && focused !== document.body && focused.isConnected) return;
|
|
75
|
+
(opened ? panelRef : triggerRef).value?.focus();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
59
78
|
return (_ctx, _cache) => {
|
|
60
|
-
return openBlock(), createElementBlock("
|
|
61
|
-
|
|
79
|
+
return !open.value ? (openBlock(), createElementBlock("button", {
|
|
80
|
+
key: 0,
|
|
81
|
+
ref_key: "triggerRef",
|
|
82
|
+
ref: triggerRef,
|
|
83
|
+
type: "button",
|
|
84
|
+
"aria-expanded": false,
|
|
85
|
+
"aria-label": `${title()} (${snapshot.value.entries.length})`,
|
|
86
|
+
"data-slot": "data-provider-devtools",
|
|
87
|
+
"data-open": false,
|
|
88
|
+
class: normalizeClass(unref(cn)(unref(buttonVariants)({
|
|
89
|
+
variant: "outline",
|
|
90
|
+
size: "icon"
|
|
91
|
+
}), "fixed right-4 bottom-4 z-50 rounded-full shadow-lg", props.class)),
|
|
92
|
+
onClick: _cache[0] || (_cache[0] = ($event) => open.value = true)
|
|
93
|
+
}, [createVNode(unref(ActivityIcon)), createElementVNode("span", _hoisted_2, toDisplayString(snapshot.value.entries.length), 1)], 10, _hoisted_1)) : (openBlock(), createElementBlock("section", {
|
|
94
|
+
key: 1,
|
|
95
|
+
ref_key: "panelRef",
|
|
96
|
+
ref: panelRef,
|
|
97
|
+
tabindex: "-1",
|
|
98
|
+
"aria-label": title(),
|
|
62
99
|
"data-slot": "data-provider-devtools",
|
|
63
|
-
"data-open":
|
|
100
|
+
"data-open": true,
|
|
64
101
|
class: normalizeClass(unref(cn)("fixed right-4 bottom-4 z-50 flex max-h-[70vh] w-96 max-w-[calc(100vw-2rem)] flex-col rounded-lg border bg-popover text-xs text-popover-foreground shadow-lg", props.class))
|
|
65
|
-
}, [createElementVNode("header",
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
}, [
|
|
69
|
-
createElementVNode("span", _hoisted_3, toDisplayString(unref(translate)("chelone.devtools.title", { _: "Data provider" })), 1),
|
|
70
|
-
createElementVNode("span", _hoisted_4, toDisplayString(snapshot.value.entries.length), 1),
|
|
102
|
+
}, [createElementVNode("header", _hoisted_4, [
|
|
103
|
+
createElementVNode("span", _hoisted_5, toDisplayString(title()), 1),
|
|
104
|
+
createElementVNode("span", _hoisted_6, toDisplayString(snapshot.value.entries.length), 1),
|
|
71
105
|
createVNode(button_default, {
|
|
72
106
|
type: "button",
|
|
73
107
|
variant: "ghost",
|
|
74
108
|
size: "sm",
|
|
75
109
|
class: "ml-auto h-6 px-2",
|
|
76
|
-
onClick: _cache[
|
|
110
|
+
onClick: _cache[1] || (_cache[1] = ($event) => props.log.clear())
|
|
77
111
|
}, {
|
|
78
112
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("chelone.devtools.clear", { _: "Clear" })), 1)]),
|
|
79
113
|
_: 1
|
|
@@ -83,18 +117,18 @@ var data_provider_devtools_vue_vue_type_script_setup_true_lang_default = /*@__PU
|
|
|
83
117
|
variant: "ghost",
|
|
84
118
|
size: "sm",
|
|
85
119
|
class: "h-6 px-2",
|
|
86
|
-
"aria-expanded":
|
|
87
|
-
onClick: _cache[
|
|
120
|
+
"aria-expanded": true,
|
|
121
|
+
onClick: _cache[2] || (_cache[2] = ($event) => open.value = false)
|
|
88
122
|
}, {
|
|
89
|
-
default: withCtx(() => [createTextVNode(toDisplayString(
|
|
123
|
+
default: withCtx(() => [createTextVNode(toDisplayString(unref(translate)("chelone.devtools.hide", { _: "Hide" })), 1)]),
|
|
90
124
|
_: 1
|
|
91
|
-
}
|
|
92
|
-
]
|
|
125
|
+
})
|
|
126
|
+
]), entries.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_7, toDisplayString(unref(translate)("chelone.devtools.empty", { _: "No calls yet" })), 1)) : (openBlock(), createElementBlock("ul", _hoisted_8, [(openBlock(true), createElementBlock(Fragment, null, renderList(entries.value, (entry) => {
|
|
93
127
|
return openBlock(), createBlock(data_provider_devtools_entry_default, {
|
|
94
128
|
key: entry.id,
|
|
95
129
|
entry
|
|
96
130
|
}, null, 8, ["entry"]);
|
|
97
|
-
}), 128))]))],
|
|
131
|
+
}), 128))]))], 10, _hoisted_3));
|
|
98
132
|
};
|
|
99
133
|
}
|
|
100
134
|
});
|
|
@@ -20,3 +20,5 @@ export declare const SendIcon: FunctionalComponent;
|
|
|
20
20
|
export declare const SquareIcon: FunctionalComponent;
|
|
21
21
|
/** The assistant close glyph (a cross). */
|
|
22
22
|
export declare const XIcon: FunctionalComponent;
|
|
23
|
+
/** The data provider devtools trigger glyph (a pulse line). */
|
|
24
|
+
export declare const ActivityIcon: FunctionalComponent;
|
|
@@ -38,4 +38,6 @@ var SendIcon = glyph(["M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0
|
|
|
38
38
|
var SquareIcon = glyph(["M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z"]);
|
|
39
39
|
/** The assistant close glyph (a cross). */
|
|
40
40
|
var XIcon = glyph(["M18 6 6 18", "m6 6 12 12"]);
|
|
41
|
-
|
|
41
|
+
/** The data provider devtools trigger glyph (a pulse line). */
|
|
42
|
+
var ActivityIcon = glyph(["M22 12h-4l-3 9L9 3l-3 9H2"]);
|
|
43
|
+
export { ActivityIcon, HouseIcon, ListIcon, LogOutIcon, MessageCircleIcon, SendIcon, SquareIcon, XIcon };
|
|
@@ -22,7 +22,9 @@ export interface MenuLiveProps {
|
|
|
22
22
|
/**
|
|
23
23
|
* A menu link with a badge counting the resource's new records. The count
|
|
24
24
|
* lives in the store, so it survives a remount; it clears while the link's
|
|
25
|
-
* route is active.
|
|
25
|
+
* route is active. Inside a `<SidebarProvider>` it is a sidebar menu button,
|
|
26
|
+
* with the label as a `title` while the desktop rail is collapsed; elsewhere it is
|
|
27
|
+
* a plain link. Divergence from the React twin: the render props are slots.
|
|
26
28
|
* Declared as a render component so the file can also hold {@link MenuLive}.
|
|
27
29
|
*/
|
|
28
30
|
export declare const MenuLiveItemLink: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
@@ -2,11 +2,17 @@ import { useCheloneContext, useCheloneStore } from "../../../context.js";
|
|
|
2
2
|
import { useTranslate } from "../../../i18n/use-translate.js";
|
|
3
3
|
import { useMatch } from "../../../router/nav-composables.js";
|
|
4
4
|
import { Badge } from "../../ui/badge.js";
|
|
5
|
-
import {
|
|
5
|
+
import { SidebarContextKey } from "../../ui/sidebar/sidebar-context.js";
|
|
6
|
+
import sidebar_menu_button_default from "../../ui/sidebar/sidebar-menu-button.js";
|
|
7
|
+
import { SidebarMenuItem } from "../../ui/sidebar/sidebar-parts.js";
|
|
8
|
+
import { Fragment, computed, createBlock, createElementBlock, defineComponent, h, inject, mergeProps, openBlock, renderList, shallowRef, unref, watchEffect } from "vue";
|
|
6
9
|
import { assertRealtimeDataProvider, clearUnread, subscribeUnreadCount, unreadKey } from "@chelone/core";
|
|
7
10
|
import { RouterLink } from "vue-router";
|
|
8
11
|
// src/components/admin/realtime/menu-live.vue?vue&type=script&setup=true&lang.ts
|
|
9
|
-
var _hoisted_1 = {
|
|
12
|
+
var _hoisted_1 = {
|
|
13
|
+
key: 1,
|
|
14
|
+
class: "flex flex-col gap-1"
|
|
15
|
+
};
|
|
10
16
|
var subscriptions = /* @__PURE__ */ new WeakMap();
|
|
11
17
|
var toEventsKey = (events) => [...new Set(events)].sort().join(",");
|
|
12
18
|
var fromEventsKey = (eventsKey) => eventsKey.split(",").filter(Boolean);
|
|
@@ -51,7 +57,9 @@ function acquire(dataProvider, store, resource, eventsKey) {
|
|
|
51
57
|
/**
|
|
52
58
|
* A menu link with a badge counting the resource's new records. The count
|
|
53
59
|
* lives in the store, so it survives a remount; it clears while the link's
|
|
54
|
-
* route is active.
|
|
60
|
+
* route is active. Inside a `<SidebarProvider>` it is a sidebar menu button,
|
|
61
|
+
* with the label as a `title` while the desktop rail is collapsed; elsewhere it is
|
|
62
|
+
* a plain link. Divergence from the React twin: the render props are slots.
|
|
55
63
|
* Declared as a render component so the file can also hold {@link MenuLive}.
|
|
56
64
|
*/
|
|
57
65
|
var MenuLiveItemLink = defineComponent({
|
|
@@ -86,6 +94,7 @@ var MenuLiveItemLink = defineComponent({
|
|
|
86
94
|
const { dataProvider } = useCheloneContext();
|
|
87
95
|
const store = useCheloneStore();
|
|
88
96
|
const translate = useTranslate();
|
|
97
|
+
const sidebar = inject(SidebarContextKey, null);
|
|
89
98
|
const match = useMatch({
|
|
90
99
|
path: props.to,
|
|
91
100
|
end: false
|
|
@@ -111,15 +120,30 @@ var MenuLiveItemLink = defineComponent({
|
|
|
111
120
|
return () => {
|
|
112
121
|
const n = count.value ?? 0;
|
|
113
122
|
const text = translate(props.label, { _: props.label });
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class: "
|
|
117
|
-
}, () =>
|
|
123
|
+
const badge = n > 0 ? h(Badge, {
|
|
124
|
+
"data-testid": "menu-live-badge",
|
|
125
|
+
class: sidebar ? "group-data-[collapsible=icon]:absolute group-data-[collapsible=icon]:top-0 group-data-[collapsible=icon]:right-0 group-data-[collapsible=icon]:h-4 group-data-[collapsible=icon]:min-w-4 group-data-[collapsible=icon]:px-1 group-data-[collapsible=icon]:text-[10px]" : void 0
|
|
126
|
+
}, () => props.badgeLabel?.(n) ?? (n > 99 ? "99+" : String(n))) : null;
|
|
127
|
+
const content = () => [
|
|
118
128
|
props.icon ? h(props.icon) : null,
|
|
119
129
|
slots.icon?.(),
|
|
120
130
|
h("span", { class: "flex-1" }, text),
|
|
121
|
-
|
|
122
|
-
]
|
|
131
|
+
badge
|
|
132
|
+
];
|
|
133
|
+
if (sidebar) return h(SidebarMenuItem, null, () => h(sidebar_menu_button_default, {
|
|
134
|
+
as: RouterLink,
|
|
135
|
+
to: props.to,
|
|
136
|
+
isActive: match.value != null,
|
|
137
|
+
class: "relative",
|
|
138
|
+
title: sidebar.state.value === "collapsed" && !sidebar.isMobile.value ? text : void 0,
|
|
139
|
+
onClick: () => {
|
|
140
|
+
if (sidebar.openMobile.value) sidebar.setOpenMobile(false);
|
|
141
|
+
}
|
|
142
|
+
}, content));
|
|
143
|
+
return h(RouterLink, {
|
|
144
|
+
to: props.to,
|
|
145
|
+
class: "flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent"
|
|
146
|
+
}, content);
|
|
123
147
|
};
|
|
124
148
|
}
|
|
125
149
|
});
|
|
@@ -128,12 +152,18 @@ var menu_live_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
|
|
|
128
152
|
props: { items: {} },
|
|
129
153
|
setup(__props) {
|
|
130
154
|
/**
|
|
131
|
-
* `<MenuLive>` — a list of {@link MenuLiveItemLink}s, one per item.
|
|
155
|
+
* `<MenuLive>` — a list of {@link MenuLiveItemLink}s, one per item. Inside a
|
|
156
|
+
* sidebar it renders the items as sidebar menu entries with no wrapper, so
|
|
157
|
+
* place it in a `<SidebarMenu>`, such as a `<ResourceMenuItemGroup>`'s default
|
|
158
|
+
* slot.
|
|
132
159
|
*/
|
|
160
|
+
const sidebar = inject(SidebarContextKey, null);
|
|
133
161
|
return (_ctx, _cache) => {
|
|
134
|
-
return
|
|
162
|
+
return unref(sidebar) ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(__props.items, (item) => {
|
|
163
|
+
return openBlock(), createBlock(unref(MenuLiveItemLink), mergeProps({ key: item.to }, { ref_for: true }, item), null, 16);
|
|
164
|
+
}), 128)) : (openBlock(), createElementBlock("nav", _hoisted_1, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
|
|
135
165
|
return openBlock(), createBlock(unref(MenuLiveItemLink), mergeProps({ key: item.to }, { ref_for: true }, item), null, 16);
|
|
136
|
-
}), 128))]);
|
|
166
|
+
}), 128))]));
|
|
137
167
|
};
|
|
138
168
|
}
|
|
139
169
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chelone/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -76,14 +76,14 @@
|
|
|
76
76
|
"access": "public"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"@chelone/core": "0.
|
|
80
|
-
"@chelone/utils": "0.
|
|
79
|
+
"@chelone/core": "0.10.0",
|
|
80
|
+
"@chelone/utils": "0.10.0",
|
|
81
81
|
"@types/geojson": "^7946.0.16",
|
|
82
82
|
"cronstrue": "^3.38.0",
|
|
83
83
|
"libphonenumber-js": "^1.13.15"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
|
-
"@chelone/supabase": "0.
|
|
86
|
+
"@chelone/supabase": "0.10.0",
|
|
87
87
|
"@dnd-kit/dom": "^0.5.3",
|
|
88
88
|
"@dnd-kit/vue": "^0.5.3",
|
|
89
89
|
"@guolao/vue-monaco-editor": "^1.6.0",
|
|
@@ -219,7 +219,7 @@
|
|
|
219
219
|
},
|
|
220
220
|
"devDependencies": {
|
|
221
221
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
222
|
-
"@chelone/supabase": "0.
|
|
222
|
+
"@chelone/supabase": "0.10.0",
|
|
223
223
|
"@chelone/test-utils": "0.0.0",
|
|
224
224
|
"@dnd-kit/dom": "^0.5.3",
|
|
225
225
|
"@dnd-kit/vue": "^0.5.3",
|