@sewadah/selaras 0.3.1 → 0.3.2
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/README.md +4 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +7 -5
- package/dist/runtime/components/AlertDialog.d.vue.ts +5 -3
- package/dist/runtime/components/AlertDialog.vue +17 -9
- package/dist/runtime/components/AlertDialog.vue.d.ts +5 -3
- package/dist/runtime/components/App.vue +2 -0
- package/dist/runtime/components/Autocomplete.d.vue.ts +2 -2
- package/dist/runtime/components/Autocomplete.vue +1 -1
- package/dist/runtime/components/Autocomplete.vue.d.ts +2 -2
- package/dist/runtime/components/Button.d.vue.ts +7 -5
- package/dist/runtime/components/Button.vue +14 -3
- package/dist/runtime/components/Button.vue.d.ts +7 -5
- package/dist/runtime/components/ColorModeSwitch.d.vue.ts +18 -0
- package/dist/runtime/components/ColorModeSwitch.vue +59 -0
- package/dist/runtime/components/ColorModeSwitch.vue.d.ts +18 -0
- package/dist/runtime/components/ConfirmRenderer.d.vue.ts +3 -0
- package/dist/runtime/components/ConfirmRenderer.vue +39 -0
- package/dist/runtime/components/ConfirmRenderer.vue.d.ts +3 -0
- package/dist/runtime/components/ContextMenu.vue +4 -2
- package/dist/runtime/components/Drawer.vue +3 -2
- package/dist/runtime/components/Dropdown.vue +4 -2
- package/dist/runtime/components/Modal.vue +3 -2
- package/dist/runtime/components/Popover.vue +4 -2
- package/dist/runtime/components/Select.d.vue.ts +8 -4
- package/dist/runtime/components/Select.vue +1 -1
- package/dist/runtime/components/Select.vue.d.ts +8 -4
- package/dist/runtime/components/Slideover.vue +3 -2
- package/dist/runtime/components/Tooltip.vue +4 -2
- package/dist/runtime/composables/use-confirm.d.ts +14 -0
- package/dist/runtime/composables/use-confirm.js +28 -0
- package/dist/runtime/internal/ComboboxSelectBase.d.vue.ts +12 -11
- package/dist/runtime/internal/ComboboxSelectBase.vue +16 -7
- package/dist/runtime/internal/ComboboxSelectBase.vue.d.ts +12 -11
- package/dist/runtime/internal/ComboboxSelectBody.d.vue.ts +8 -7
- package/dist/runtime/internal/ComboboxSelectBody.vue +4 -4
- package/dist/runtime/internal/ComboboxSelectBody.vue.d.ts +8 -7
- package/dist/runtime/internal/combobox-select.d.ts +19 -18
- package/dist/runtime/internal/combobox-select.js +36 -14
- package/dist/runtime/internal/programmatic-services.d.ts +11 -0
- package/dist/runtime/internal/programmatic-services.js +1 -0
- package/dist/runtime/theme/badge.d.ts +9 -0
- package/dist/runtime/theme/badge.js +2 -1
- package/dist/runtime/theme/button.d.ts +18 -0
- package/dist/runtime/theme/button.js +4 -1
- package/dist/runtime/theme/toast.js +1 -1
- package/dist/runtime/theme-config.d.ts +1 -1
- package/dist/runtime/types/index.d.ts +4 -1
- package/dist/runtime/utils/autocomplete-contracts.d.ts +4 -4
- package/dist/runtime/utils/file-icons.js +1 -0
- package/dist/runtime/utils/messages.d.ts +1 -0
- package/dist/runtime/utils/messages.js +1 -0
- package/dist/runtime/utils/select-contracts.d.ts +18 -10
- package/dist/runtime/utils/ui.d.ts +11 -0
- package/dist/runtime/utils/ui.js +54 -1
- package/package.json +5 -5
|
@@ -2,30 +2,42 @@ import { computed } from "vue";
|
|
|
2
2
|
function optionValue(value) {
|
|
3
3
|
if (typeof value === "string" || typeof value === "number" && Number.isFinite(value))
|
|
4
4
|
return value;
|
|
5
|
-
|
|
5
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value))
|
|
6
|
+
return value;
|
|
7
|
+
throw new TypeError("[selaras] Select option values must be strings, finite numbers, or objects.");
|
|
6
8
|
}
|
|
7
9
|
export function isOptionGroup(entry) {
|
|
8
|
-
return typeof entry.label === "string" && "items" in entry && Array.isArray(entry.items);
|
|
10
|
+
return typeof entry === "object" && entry !== null && !Array.isArray(entry) && "label" in entry && typeof entry.label === "string" && "items" in entry && Array.isArray(entry.items);
|
|
9
11
|
}
|
|
10
12
|
export function flattenItems(items) {
|
|
11
13
|
return items.flatMap((entry) => isOptionGroup(entry) ? entry.items : [entry]);
|
|
12
14
|
}
|
|
13
15
|
export function useComboboxSelect(props, emit, { creatable = false } = {}) {
|
|
14
|
-
const valueKey = computed(() => props.valueKey
|
|
16
|
+
const valueKey = computed(() => props.valueKey);
|
|
15
17
|
const labelKey = computed(() => props.labelKey ?? "label");
|
|
16
18
|
const flatOptions = computed(() => {
|
|
17
19
|
const identities = /* @__PURE__ */ new Set();
|
|
18
|
-
return flattenItems(props.items).map((raw) => {
|
|
19
|
-
|
|
20
|
+
return flattenItems(props.items).map((raw, index) => {
|
|
21
|
+
if (typeof raw === "string" || typeof raw === "number") {
|
|
22
|
+
const value2 = optionValue(raw);
|
|
23
|
+
if (identities.has(value2))
|
|
24
|
+
throw new TypeError(`[selaras] Select option identities must be unique across all groups. Duplicate value: ${typeof value2} ${JSON.stringify(value2)}.`);
|
|
25
|
+
identities.add(value2);
|
|
26
|
+
return { value: value2, key: `${typeof value2}:${String(value2)}`, label: String(value2), disabled: false, raw };
|
|
27
|
+
}
|
|
28
|
+
const option = raw;
|
|
29
|
+
const key = valueKey.value ?? (option.value !== void 0 ? "value" : void 0);
|
|
30
|
+
const value = optionValue(key === void 0 ? option : option[key]);
|
|
20
31
|
if (identities.has(value)) {
|
|
21
|
-
throw new TypeError(`[selaras] Select option identities must be unique across all groups. Duplicate ${
|
|
32
|
+
throw new TypeError(`[selaras] Select option identities must be unique across all groups. Duplicate ${key ?? "object identity"}: ${typeof value} ${JSON.stringify(value)}.`);
|
|
22
33
|
}
|
|
23
34
|
identities.add(value);
|
|
24
35
|
return {
|
|
25
36
|
value,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
key: typeof value === "object" ? `object:${index}` : `${typeof value}:${String(value)}`,
|
|
38
|
+
label: String(option[labelKey.value] ?? (typeof value === "object" ? "" : value)),
|
|
39
|
+
disabled: !!option.disabled,
|
|
40
|
+
raw: option
|
|
29
41
|
};
|
|
30
42
|
});
|
|
31
43
|
});
|
|
@@ -36,14 +48,24 @@ export function useComboboxSelect(props, emit, { creatable = false } = {}) {
|
|
|
36
48
|
return Array.isArray(v) ? v : [v];
|
|
37
49
|
});
|
|
38
50
|
function resolveOption(value) {
|
|
39
|
-
|
|
51
|
+
const option = flatOptions.value.find((o) => o.value === value);
|
|
52
|
+
if (option)
|
|
53
|
+
return option;
|
|
54
|
+
const label = typeof value === "object" ? value[labelKey.value] : value;
|
|
55
|
+
return { value, key: typeof value === "object" ? "missing-object" : `${typeof value}:${String(value)}`, label: String(label ?? ""), disabled: false, raw: void 0 };
|
|
40
56
|
}
|
|
41
57
|
function toOption(raw) {
|
|
58
|
+
if (typeof raw === "string" || typeof raw === "number")
|
|
59
|
+
return { value: optionValue(raw), key: `${typeof raw}:${String(raw)}`, label: String(raw), disabled: false, raw };
|
|
60
|
+
const option = raw;
|
|
61
|
+
const key = valueKey.value ?? (option.value !== void 0 ? "value" : void 0);
|
|
62
|
+
const value = optionValue(key === void 0 ? option : option[key]);
|
|
42
63
|
return {
|
|
43
|
-
value
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
value,
|
|
65
|
+
key: typeof value === "object" ? "object:unknown" : `${typeof value}:${String(value)}`,
|
|
66
|
+
label: String(option[labelKey.value] ?? (typeof value === "object" ? "" : value)),
|
|
67
|
+
disabled: !!option.disabled,
|
|
68
|
+
raw: option
|
|
47
69
|
};
|
|
48
70
|
}
|
|
49
71
|
const selectedOptions = computed(() => selectedValues.value.map(resolveOption));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Component, Ref } from 'vue';
|
|
2
2
|
import type { ToastOptions } from '../composables/use-toast.js';
|
|
3
|
+
import type { ColorRole } from '../utils/color-registry.js';
|
|
3
4
|
import type { ProgrammaticThemeSnapshot } from '../utils/programmatic-theme.js';
|
|
4
5
|
interface ProgrammaticOverlayInstance {
|
|
5
6
|
id: number;
|
|
@@ -47,6 +48,15 @@ export interface SlideoverInstance extends ProgrammaticOverlayInstance {
|
|
|
47
48
|
transition?: boolean;
|
|
48
49
|
_theme: ProgrammaticThemeSnapshot;
|
|
49
50
|
}
|
|
51
|
+
export interface ConfirmInstance extends ProgrammaticOverlayInstance {
|
|
52
|
+
title: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
confirmLabel?: string;
|
|
55
|
+
cancelLabel?: string;
|
|
56
|
+
confirmColor?: ColorRole;
|
|
57
|
+
icon?: string;
|
|
58
|
+
_theme: ProgrammaticThemeSnapshot;
|
|
59
|
+
}
|
|
50
60
|
export interface ToastItem extends ToastOptions {
|
|
51
61
|
id: number;
|
|
52
62
|
_theme: ProgrammaticThemeSnapshot;
|
|
@@ -69,5 +79,6 @@ export interface ToastService {
|
|
|
69
79
|
export declare const useModalService: () => ProgrammaticOverlayService<ModalInstance>;
|
|
70
80
|
export declare const useDrawerService: () => ProgrammaticOverlayService<DrawerInstance>;
|
|
71
81
|
export declare const useSlideoverService: () => ProgrammaticOverlayService<SlideoverInstance>;
|
|
82
|
+
export declare const useConfirmService: () => ProgrammaticOverlayService<ConfirmInstance>;
|
|
72
83
|
export declare function useToastService(): ToastService;
|
|
73
84
|
export {};
|
|
@@ -40,6 +40,7 @@ function createOverlayService() {
|
|
|
40
40
|
export const useModalService = createOverlayService();
|
|
41
41
|
export const useDrawerService = createOverlayService();
|
|
42
42
|
export const useSlideoverService = createOverlayService();
|
|
43
|
+
export const useConfirmService = createOverlayService();
|
|
43
44
|
const useToastState = createAppScopedState(() => ({
|
|
44
45
|
toasts: ref([]),
|
|
45
46
|
max: ref(5),
|
|
@@ -18,6 +18,9 @@ export declare const badgeTheme: import("tailwind-variants").TVReturnType<{
|
|
|
18
18
|
outline: {
|
|
19
19
|
base: string;
|
|
20
20
|
};
|
|
21
|
+
subtle: {
|
|
22
|
+
base: string;
|
|
23
|
+
};
|
|
21
24
|
};
|
|
22
25
|
size: {
|
|
23
26
|
sm: {
|
|
@@ -74,6 +77,9 @@ export declare const badgeTheme: import("tailwind-variants").TVReturnType<{
|
|
|
74
77
|
outline: {
|
|
75
78
|
base: string;
|
|
76
79
|
};
|
|
80
|
+
subtle: {
|
|
81
|
+
base: string;
|
|
82
|
+
};
|
|
77
83
|
};
|
|
78
84
|
size: {
|
|
79
85
|
sm: {
|
|
@@ -130,6 +136,9 @@ export declare const badgeTheme: import("tailwind-variants").TVReturnType<{
|
|
|
130
136
|
outline: {
|
|
131
137
|
base: string;
|
|
132
138
|
};
|
|
139
|
+
subtle: {
|
|
140
|
+
base: string;
|
|
141
|
+
};
|
|
133
142
|
};
|
|
134
143
|
size: {
|
|
135
144
|
sm: {
|
|
@@ -25,7 +25,8 @@ export const badgeTheme = tv({
|
|
|
25
25
|
variant: {
|
|
26
26
|
solid: { base: "bg-[var(--_selaras-color-fill)] text-[var(--_selaras-color-on-fill)]" },
|
|
27
27
|
soft: { base: "bg-[var(--_selaras-color-subtle)] text-[var(--_selaras-color-on-subtle)]" },
|
|
28
|
-
outline: { base: "ring-1 ring-inset ring-[var(--_selaras-color-border)] text-[var(--_selaras-color-text)]" }
|
|
28
|
+
outline: { base: "ring-1 ring-inset ring-[var(--_selaras-color-border)] text-[var(--_selaras-color-text)]" },
|
|
29
|
+
subtle: { base: "bg-[var(--selaras-resolved-surface-elevated)] ring-1 ring-[var(--selaras-resolved-border-default)] text-[var(--_selaras-color-text)]" }
|
|
29
30
|
},
|
|
30
31
|
size: {
|
|
31
32
|
sm: { base: "h-4 px-1 text-xs", leadingIcon: "size-2.5", trailingIcon: "size-2.5", dot: "size-1.5" },
|
|
@@ -15,6 +15,9 @@ export declare const buttonTheme: import("tailwind-variants").TVReturnType<{
|
|
|
15
15
|
soft: {
|
|
16
16
|
base: string;
|
|
17
17
|
};
|
|
18
|
+
subtle: {
|
|
19
|
+
base: string;
|
|
20
|
+
};
|
|
18
21
|
outline: {
|
|
19
22
|
base: string;
|
|
20
23
|
};
|
|
@@ -24,6 +27,9 @@ export declare const buttonTheme: import("tailwind-variants").TVReturnType<{
|
|
|
24
27
|
text: {
|
|
25
28
|
base: string;
|
|
26
29
|
};
|
|
30
|
+
link: {
|
|
31
|
+
base: string;
|
|
32
|
+
};
|
|
27
33
|
};
|
|
28
34
|
size: {
|
|
29
35
|
sm: {
|
|
@@ -76,6 +82,9 @@ export declare const buttonTheme: import("tailwind-variants").TVReturnType<{
|
|
|
76
82
|
soft: {
|
|
77
83
|
base: string;
|
|
78
84
|
};
|
|
85
|
+
subtle: {
|
|
86
|
+
base: string;
|
|
87
|
+
};
|
|
79
88
|
outline: {
|
|
80
89
|
base: string;
|
|
81
90
|
};
|
|
@@ -85,6 +94,9 @@ export declare const buttonTheme: import("tailwind-variants").TVReturnType<{
|
|
|
85
94
|
text: {
|
|
86
95
|
base: string;
|
|
87
96
|
};
|
|
97
|
+
link: {
|
|
98
|
+
base: string;
|
|
99
|
+
};
|
|
88
100
|
};
|
|
89
101
|
size: {
|
|
90
102
|
sm: {
|
|
@@ -137,6 +149,9 @@ export declare const buttonTheme: import("tailwind-variants").TVReturnType<{
|
|
|
137
149
|
soft: {
|
|
138
150
|
base: string;
|
|
139
151
|
};
|
|
152
|
+
subtle: {
|
|
153
|
+
base: string;
|
|
154
|
+
};
|
|
140
155
|
outline: {
|
|
141
156
|
base: string;
|
|
142
157
|
};
|
|
@@ -146,6 +161,9 @@ export declare const buttonTheme: import("tailwind-variants").TVReturnType<{
|
|
|
146
161
|
text: {
|
|
147
162
|
base: string;
|
|
148
163
|
};
|
|
164
|
+
link: {
|
|
165
|
+
base: string;
|
|
166
|
+
};
|
|
149
167
|
};
|
|
150
168
|
size: {
|
|
151
169
|
sm: {
|
|
@@ -19,10 +19,13 @@ export const buttonTheme = tv({
|
|
|
19
19
|
variant: {
|
|
20
20
|
solid: { base: "bg-[var(--_selaras-color-fill)] text-[var(--_selaras-color-on-fill)] hover:bg-[var(--_selaras-color-fill-hover)] active:bg-[var(--_selaras-color-fill-pressed)]" },
|
|
21
21
|
soft: { base: "bg-[var(--_selaras-color-subtle)] text-[var(--_selaras-color-on-subtle)] hover:bg-[var(--_selaras-color-subtle-hover)] active:bg-[var(--_selaras-color-subtle-pressed)]" },
|
|
22
|
+
subtle: { base: "bg-[var(--_selaras-color-subtle)] text-[var(--_selaras-color-on-subtle)] ring-1 ring-inset ring-[var(--_selaras-color-border)] hover:bg-[var(--_selaras-color-subtle-hover)] active:bg-[var(--_selaras-color-subtle-pressed)]" },
|
|
22
23
|
outline: { base: "ring-1 ring-inset ring-[var(--_selaras-color-border)] text-[var(--_selaras-color-text)] hover:bg-[var(--_selaras-color-subtle-hover)] active:bg-[var(--_selaras-color-subtle-pressed)]" },
|
|
23
24
|
ghost: { base: "text-[var(--_selaras-color-text)] hover:bg-[var(--_selaras-color-subtle-hover)] active:bg-[var(--_selaras-color-subtle-pressed)]" },
|
|
24
25
|
// Text actions never paint a background, including their interaction states.
|
|
25
|
-
text: { base: "text-[var(--_selaras-color-text)] hover:text-[var(--_selaras-color-text-hover)] active:text-[var(--_selaras-color-text-pressed)]" }
|
|
26
|
+
text: { base: "text-[var(--_selaras-color-text)] hover:text-[color-mix(in_srgb,var(--_selaras-color-text-hover)_60%,var(--selaras-resolved-text-default))] active:text-[color-mix(in_srgb,var(--_selaras-color-text-pressed)_40%,var(--selaras-resolved-text-default))]" },
|
|
27
|
+
// Link actions underline on hover while keeping text's interaction treatment.
|
|
28
|
+
link: { base: "text-[var(--_selaras-color-text)] underline-offset-4 hover:underline hover:text-[color-mix(in_srgb,var(--_selaras-color-text-hover)_60%,var(--selaras-resolved-text-default))] active:text-[color-mix(in_srgb,var(--_selaras-color-text-pressed)_40%,var(--selaras-resolved-text-default))]" }
|
|
26
29
|
},
|
|
27
30
|
size: {
|
|
28
31
|
sm: { base: "h-8 px-3 text-sm", leadingIcon: "size-4", trailingIcon: "size-4" },
|
|
@@ -10,7 +10,7 @@ export const toastTheme = tv({
|
|
|
10
10
|
// Cards behind the front one cover their own content with an overlay
|
|
11
11
|
// of the card surface (after:), so they read as a clean pile and fade
|
|
12
12
|
// their content back in when they move to the front or fan out.
|
|
13
|
-
root: "absolute inset-x-4 flex items-start gap-3 rounded-[var(--selaras-resolved-radius-md)] bg-[var(--_selaras-color-subtle,var(--selaras-resolved-surface-default))] p-4 pe-10 shadow-[var(--selaras-resolved-shadow-lg)] ring-1 ring-[var(--selaras-resolved-border-default)] transition-[transform,opacity,height] duration-400 ease-[cubic-bezier(0.22,1,0.36,1)] after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:
|
|
13
|
+
root: "absolute inset-x-4 flex items-start gap-3 rounded-[var(--selaras-resolved-radius-md)] bg-[var(--_selaras-color-subtle,var(--selaras-resolved-surface-default))] p-4 pe-10 shadow-[var(--selaras-resolved-shadow-lg)] ring-1 ring-[var(--selaras-resolved-border-default)] transition-[transform,opacity,height] duration-400 ease-[cubic-bezier(0.22,1,0.36,1)] after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:[background:inherit] after:opacity-0 after:transition-opacity after:duration-400 data-[stack=behind]:overflow-hidden data-[stack=behind]:after:opacity-100 data-[stack=hidden]:pointer-events-none data-[stack=hidden]:overflow-hidden data-[stack=hidden]:opacity-0 data-[stack=hidden]:after:opacity-100 data-[state=open]:animate-[selaras-toast-in_400ms_cubic-bezier(0.22,1,0.36,1)] data-[state=closed]:animate-[selaras-toast-out_200ms_ease-in_forwards] data-[stack=behind]:[--_selaras-toast-exit:-25%] data-[stack=hidden]:[--_selaras-toast-exit:-25%] motion-reduce:transition-none motion-reduce:animate-none motion-reduce:after:transition-none",
|
|
14
14
|
title: "text-sm font-medium text-[var(--selaras-resolved-text-default)]",
|
|
15
15
|
description: "mt-1 text-sm text-[var(--selaras-resolved-text-muted)]",
|
|
16
16
|
// Base (no color set) is a plain neutral glyph - only shown at all when
|
|
@@ -265,7 +265,7 @@ export interface ThemeComponentRegistry {
|
|
|
265
265
|
};
|
|
266
266
|
chip: {
|
|
267
267
|
slots: ChipThemeSlots;
|
|
268
|
-
conditions: WithRegisteredColor<Pick<ChipVariants, 'color' | 'size'>>;
|
|
268
|
+
conditions: WithRegisteredColor<Pick<ChipVariants, 'color' | 'variant' | 'size'>>;
|
|
269
269
|
defaults: WithRegisteredColor<Pick<ChipVariants, 'color' | 'size'>>;
|
|
270
270
|
};
|
|
271
271
|
collapsible: {
|
|
@@ -19,6 +19,7 @@ export type * from '../components/CodeButton.vue';
|
|
|
19
19
|
export type * from '../components/CodeGroup.vue';
|
|
20
20
|
export type * from '../components/CodeTree.vue';
|
|
21
21
|
export type * from '../components/Collapsible.vue';
|
|
22
|
+
export type * from '../components/ColorModeSwitch.vue';
|
|
22
23
|
export type * from '../components/ColorModeToggle.vue';
|
|
23
24
|
export type * from '../components/ColorPicker.vue';
|
|
24
25
|
export type * from '../components/CommandPalette.vue';
|
|
@@ -84,6 +85,7 @@ export type * from '../components/ToggleGroup.vue';
|
|
|
84
85
|
export type * from '../components/Tooltip.vue';
|
|
85
86
|
export type * from '../components/Tree.vue';
|
|
86
87
|
export type { UseCommandPaletteReturn } from '../composables/use-command-palette.js';
|
|
88
|
+
export type { UseConfirmOptions, UseConfirmReturn } from '../composables/use-confirm.js';
|
|
87
89
|
export type { UseDrawerOpenOptions, UseDrawerReturn } from '../composables/use-drawer.js';
|
|
88
90
|
export type { UseModalOpenOptions, UseModalReturn } from '../composables/use-modal.js';
|
|
89
91
|
export type { UseSlideoverOpenOptions, UseSlideoverReturn } from '../composables/use-slideover.js';
|
|
@@ -98,8 +100,9 @@ export type { IconRegistry } from '../utils/icons.js';
|
|
|
98
100
|
export { defaultIcons } from '../utils/icons.js';
|
|
99
101
|
export type { MessageRegistry } from '../utils/messages.js';
|
|
100
102
|
export { defaultMessages } from '../utils/messages.js';
|
|
103
|
+
export type { NavigationMenuItem } from '../utils/navigation-menu.js';
|
|
101
104
|
export type { ContextMenuPositioning, OverlayPortal, OverlayPositioning } from '../utils/overlay.js';
|
|
102
|
-
export type { SelectGroup, SelectIdentity, SelectModel, SelectResolvedOption, SelectValue } from '../utils/select-contracts.js';
|
|
105
|
+
export type { SelectGroup, SelectIdentity, SelectModel, SelectModelValue, SelectResolvedOption, SelectValue } from '../utils/select-contracts.js';
|
|
103
106
|
export type { TableEmits, TableProps, TableSlots } from '../utils/table-contracts.js';
|
|
104
107
|
export type { TimelineItem, TimelineProps, TimelineSlotProps, TimelineSlots } from '../utils/timeline-contracts.js';
|
|
105
108
|
export type { SelarasRuntimeConfig } from './app-config.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { SelectIdentity, SelectModel, SelectProps, SelectSlots, SelectValue } from './select-contracts.js';
|
|
1
|
+
import type { SelectEntry, SelectIdentity, SelectModel, SelectProps, SelectSlots, SelectValue } from './select-contracts.js';
|
|
2
2
|
export type AutocompleteValue<Entry, Key extends string = 'value', Forced extends boolean = false> = Forced extends true ? SelectIdentity<Entry, Key> : SelectIdentity<Entry, Key> | string;
|
|
3
|
-
export interface AutocompleteProps<Entry extends
|
|
3
|
+
export interface AutocompleteProps<Entry extends SelectEntry = {
|
|
4
4
|
value: SelectValue;
|
|
5
5
|
label?: string;
|
|
6
6
|
disabled?: boolean;
|
|
@@ -13,7 +13,7 @@ export interface AutocompleteProps<Entry extends object = {
|
|
|
13
13
|
/** Blocks unmatched free-text commits; does not require async option membership. */
|
|
14
14
|
forceSelection?: Forced;
|
|
15
15
|
}
|
|
16
|
-
export interface AutocompleteEmits<Entry extends
|
|
16
|
+
export interface AutocompleteEmits<Entry extends SelectEntry = {
|
|
17
17
|
value: SelectValue;
|
|
18
18
|
}, Key extends string = 'value', Multiple extends boolean = false, Forced extends boolean = false> {
|
|
19
19
|
'update:open': [value: boolean];
|
|
@@ -21,4 +21,4 @@ export interface AutocompleteEmits<Entry extends object = {
|
|
|
21
21
|
'update:searchTerm': [value: string];
|
|
22
22
|
}
|
|
23
23
|
/** Autocomplete's editable input has no value or filter-icon slot. */
|
|
24
|
-
export type AutocompleteSlots<Entry extends
|
|
24
|
+
export type AutocompleteSlots<Entry extends SelectEntry> = Omit<SelectSlots<Entry>, 'value' | 'filter-icon'>;
|
|
@@ -108,6 +108,7 @@ export const defaultFileIcons = {
|
|
|
108
108
|
"npm": "vscode-icons:file-type-npm",
|
|
109
109
|
"pnpm": "vscode-icons:file-type-pnpm",
|
|
110
110
|
"yarn": "vscode-icons:file-type-yarn",
|
|
111
|
+
"bun": "vscode-icons:file-type-bun",
|
|
111
112
|
"nuxt": "vscode-icons:file-type-nuxt",
|
|
112
113
|
"ansible": "vscode-icons:file-type-ansible",
|
|
113
114
|
"bicep": "vscode-icons:file-type-bicep"
|
|
@@ -5,19 +5,25 @@ import type { ColorRole } from './color-registry.js';
|
|
|
5
5
|
import type { OverlayPortal, OverlayPositioning } from './overlay.js';
|
|
6
6
|
import type { UiProp } from './ui.js';
|
|
7
7
|
export type SelectValue = string | number;
|
|
8
|
-
export
|
|
8
|
+
export type SelectModelValue = SelectValue | object;
|
|
9
|
+
export type SelectEntry = SelectValue | object;
|
|
10
|
+
export interface SelectGroup<Item extends SelectEntry> {
|
|
9
11
|
label: string;
|
|
10
12
|
items: readonly Item[];
|
|
11
13
|
}
|
|
12
14
|
/** Infer options from the entry array before unwrapping one group level. */
|
|
13
15
|
export type SelectEntryItem<Entry> = Entry extends SelectGroup<infer Item> ? Item : Entry;
|
|
14
|
-
export type SelectEntryGroup<Entry> = Extract<Entry, SelectGroup<
|
|
15
|
-
export type SelectIdentityKeys<Item> = {
|
|
16
|
-
[Key in keyof Item]-?: Item[Key] extends
|
|
17
|
-
}[keyof Item] & string;
|
|
18
|
-
|
|
16
|
+
export type SelectEntryGroup<Entry> = Extract<Entry, SelectGroup<SelectEntry>>;
|
|
17
|
+
export type SelectIdentityKeys<Item> = [Item] extends [object] ? {
|
|
18
|
+
[Key in keyof Item]-?: Item[Key] extends SelectModelValue ? Key : never;
|
|
19
|
+
}[keyof Item] & string : never;
|
|
20
|
+
type DefaultObjectIdentity<Item extends object> = 'value' extends keyof Item ? Item extends {
|
|
21
|
+
value: infer Value;
|
|
22
|
+
} ? Value : NonNullable<Item['value' & keyof Item]> | Item : Item;
|
|
23
|
+
type ItemIdentity<Item, Key extends string> = Item extends SelectValue ? Item : Key extends 'value' ? Item extends object ? DefaultObjectIdentity<Item> : never : Key extends keyof Item ? Item[Key] : never;
|
|
24
|
+
export type SelectIdentity<Entry, Key extends string = 'value'> = ItemIdentity<SelectEntryItem<Entry>, Key>;
|
|
19
25
|
export type SelectModel<Value, Multiple extends boolean> = Multiple extends true ? Value[] : Value | undefined;
|
|
20
|
-
type ValidEntry<Entry, Key extends string> = Entry extends SelectGroup<infer Item> ? SelectGroup<Item & Record<Key,
|
|
26
|
+
type ValidEntry<Entry, Key extends string> = Entry extends SelectGroup<infer Item> ? SelectGroup<Item extends SelectValue ? Item : Key extends 'value' ? Item : Item & Record<Key, SelectModelValue>> : Entry extends SelectValue ? Entry : Key extends 'value' ? Entry : Entry & Record<Key, SelectModelValue>;
|
|
21
27
|
export interface SelectResolvedOption<Entry, Key extends string = 'value'> {
|
|
22
28
|
value: SelectIdentity<Entry, Key>;
|
|
23
29
|
label: string;
|
|
@@ -25,7 +31,7 @@ export interface SelectResolvedOption<Entry, Key extends string = 'value'> {
|
|
|
25
31
|
/** Undefined while a selected identity is absent from the current options. */
|
|
26
32
|
raw: SelectEntryItem<Entry> | undefined;
|
|
27
33
|
}
|
|
28
|
-
export interface SelectProps<Entry extends
|
|
34
|
+
export interface SelectProps<Entry extends SelectEntry = {
|
|
29
35
|
value: SelectValue;
|
|
30
36
|
label?: string;
|
|
31
37
|
disabled?: boolean;
|
|
@@ -35,7 +41,9 @@ export interface SelectProps<Entry extends object = {
|
|
|
35
41
|
/** ID of an associated form outside the component's ancestors. */
|
|
36
42
|
form?: string;
|
|
37
43
|
items: readonly Entry[] & NoInfer<readonly ValidEntry<Entry, Key>[]>;
|
|
44
|
+
/** Uses an option's `value` field by default, or the whole object if absent. */
|
|
38
45
|
valueKey?: Key & SelectIdentityKeys<SelectEntryItem<Entry>>;
|
|
46
|
+
/** Object field used for display; defaults to `label`. */
|
|
39
47
|
labelKey?: keyof SelectEntryItem<Entry> & string;
|
|
40
48
|
open?: boolean;
|
|
41
49
|
/** Initial uncontrolled open state. */
|
|
@@ -78,14 +86,14 @@ export interface SelectProps<Entry extends object = {
|
|
|
78
86
|
adaptive?: boolean;
|
|
79
87
|
ui?: UiProp<SelectThemeSlots>;
|
|
80
88
|
}
|
|
81
|
-
export interface SelectEmits<Entry extends
|
|
89
|
+
export interface SelectEmits<Entry extends SelectEntry = {
|
|
82
90
|
value: SelectValue;
|
|
83
91
|
}, Key extends string = 'value', Multiple extends boolean = false> {
|
|
84
92
|
'update:open': [value: boolean];
|
|
85
93
|
'update:modelValue': [value: SelectModel<SelectIdentity<Entry, Key>, Multiple>];
|
|
86
94
|
'update:searchTerm': [value: string];
|
|
87
95
|
}
|
|
88
|
-
export interface SelectSlots<Entry extends
|
|
96
|
+
export interface SelectSlots<Entry extends SelectEntry, Key extends string = 'value'> {
|
|
89
97
|
'item'?: (props: {
|
|
90
98
|
item: SelectEntryItem<Entry>;
|
|
91
99
|
}) => any;
|
|
@@ -107,6 +107,10 @@ export declare function useRootProps<T extends AnySlotFn>(slotFn: () => T, overr
|
|
|
107
107
|
}): ComputedRef<{
|
|
108
108
|
[x: string]: unknown;
|
|
109
109
|
}>;
|
|
110
|
+
/** Routes fallthrough attrs to a chosen non-root target without theme bindings. */
|
|
111
|
+
export declare function useFallthroughProps(override?: () => UiSlotValue | undefined): ComputedRef<{
|
|
112
|
+
[x: string]: unknown;
|
|
113
|
+
}>;
|
|
110
114
|
/**
|
|
111
115
|
* Returns fallthrough attributes selected for a component's real interactive
|
|
112
116
|
* element. Wrapped controls use this alongside useRootProps: layout and
|
|
@@ -124,6 +128,13 @@ export declare function useFallthroughAttrs(select: (key: string) => boolean): C
|
|
|
124
128
|
* to the base theme untouched when neither exists.
|
|
125
129
|
*/
|
|
126
130
|
export declare function useComponentTheme<T extends (...args: any[]) => any>(key: string, base: T): ComputedRef<T>;
|
|
131
|
+
/**
|
|
132
|
+
* Tailwind Variants drops the classes for an axis when its supplied value is
|
|
133
|
+
* unknown. Keep malformed runtime props usable by resolving them to the
|
|
134
|
+
* active theme's own default, and report the mistake once in development.
|
|
135
|
+
* Theme-configured variant values and compound-variant values stay valid.
|
|
136
|
+
*/
|
|
137
|
+
export declare function withVariantFallback<T extends (...args: any[]) => any>(theme: T, key: string): T;
|
|
127
138
|
/**
|
|
128
139
|
* The nearest (chain-merged) STheme `defaults.<key>` override, if any - a
|
|
129
140
|
* component reads this as its own prop's fallback, one level below its
|
package/dist/runtime/utils/ui.js
CHANGED
|
@@ -53,6 +53,13 @@ export function useRootProps(slotFn, override, options) {
|
|
|
53
53
|
resolveSlot(slotFn(), withFallthroughClass(options?.includeClass === false ? void 0 : fallthroughClass.value, override()))
|
|
54
54
|
));
|
|
55
55
|
}
|
|
56
|
+
export function useFallthroughProps(override) {
|
|
57
|
+
const { fallthroughClass, attrsWithoutClass } = useRootFallthrough();
|
|
58
|
+
return computed(() => mergeProps(
|
|
59
|
+
attrsWithoutClass.value,
|
|
60
|
+
resolveSlot((options) => options?.class ?? "", withFallthroughClass(fallthroughClass.value, override?.()))
|
|
61
|
+
));
|
|
62
|
+
}
|
|
56
63
|
export function useFallthroughAttrs(select) {
|
|
57
64
|
const attrs = useAttrs();
|
|
58
65
|
return computed(() => Object.fromEntries(
|
|
@@ -78,7 +85,53 @@ export function useComponentTheme(key, base) {
|
|
|
78
85
|
result = tv({ extend: result, ...globalOverride });
|
|
79
86
|
for (const override of collectThemeChain(themeContext?.value, (c) => c.ui, key))
|
|
80
87
|
result = tv({ extend: result, ...override });
|
|
81
|
-
return result;
|
|
88
|
+
return withVariantFallback(result, key);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const warnedThemeValues = /* @__PURE__ */ new Set();
|
|
92
|
+
export function withVariantFallback(theme, key) {
|
|
93
|
+
return new Proxy(theme, {
|
|
94
|
+
apply(target, thisArg, args) {
|
|
95
|
+
const supplied = args[0];
|
|
96
|
+
if (!supplied || typeof supplied !== "object")
|
|
97
|
+
return Reflect.apply(target, thisArg, args);
|
|
98
|
+
let resolved;
|
|
99
|
+
for (const axis of ["variant", "size"]) {
|
|
100
|
+
const value = supplied[axis];
|
|
101
|
+
if (value === void 0)
|
|
102
|
+
continue;
|
|
103
|
+
const values = target.variants?.[axis];
|
|
104
|
+
if (!values)
|
|
105
|
+
continue;
|
|
106
|
+
const accepted = new Set(Object.keys(values));
|
|
107
|
+
for (const compound of target.compoundVariants ?? []) {
|
|
108
|
+
const condition = compound[axis];
|
|
109
|
+
if (Array.isArray(condition)) {
|
|
110
|
+
for (const entry of condition)
|
|
111
|
+
accepted.add(String(entry));
|
|
112
|
+
} else if (condition !== void 0) {
|
|
113
|
+
accepted.add(String(condition));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (accepted.has(String(value)))
|
|
117
|
+
continue;
|
|
118
|
+
const defaults = target.defaultVariants;
|
|
119
|
+
const fallback = defaults?.[axis];
|
|
120
|
+
resolved ??= { ...supplied };
|
|
121
|
+
resolved[axis] = fallback;
|
|
122
|
+
if (import.meta.dev) {
|
|
123
|
+
const signature = `${key}:${axis}:${String(value)}`;
|
|
124
|
+
if (!warnedThemeValues.has(signature)) {
|
|
125
|
+
warnedThemeValues.add(signature);
|
|
126
|
+
const component = `${key[0]?.toUpperCase() ?? ""}${key.slice(1)}`;
|
|
127
|
+
const values2 = [...accepted].join(", ");
|
|
128
|
+
const fallbackMessage = fallback === void 0 ? "the default styling" : `"${String(fallback)}"`;
|
|
129
|
+
console.warn(`[Selaras ${component}] Unknown ${axis} "${String(value)}"; using ${fallbackMessage}. Valid values: ${values2}.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return Reflect.apply(target, thisArg, resolved ? [resolved, ...args.slice(1)] : args);
|
|
134
|
+
}
|
|
82
135
|
});
|
|
83
136
|
}
|
|
84
137
|
export function useThemeProps(key) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sewadah/selaras",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.2",
|
|
5
5
|
"description": "A UI component library for Nuxt, built on Reka UI and Tailwind v4",
|
|
6
6
|
"author": "Wicaksana Pratama <wicaksanapratama@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"import": "./dist/runtime/theme-api.js"
|
|
46
46
|
},
|
|
47
47
|
"./components/DrawerRenderer*": {},
|
|
48
|
+
"./components/ConfirmRenderer*": {},
|
|
48
49
|
"./components/ModalRenderer*": {},
|
|
49
50
|
"./components/NavigationMenuAccordionItem*": {},
|
|
50
51
|
"./components/NavigationMenuFlyoutList*": {},
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
"./dist/runtime/theme-api.d.ts"
|
|
77
78
|
],
|
|
78
79
|
"components/DrawerRenderer*": [],
|
|
80
|
+
"components/ConfirmRenderer*": [],
|
|
79
81
|
"components/ModalRenderer*": [],
|
|
80
82
|
"components/NavigationMenuAccordionItem*": [],
|
|
81
83
|
"components/NavigationMenuFlyoutList*": [],
|
|
@@ -98,18 +100,16 @@
|
|
|
98
100
|
],
|
|
99
101
|
"scripts": {
|
|
100
102
|
"prepack": "nuxt-module-build build",
|
|
101
|
-
"dev": "bun run docs:dev",
|
|
103
|
+
"dev": "bun run docs:prepare && nuxt dev docs",
|
|
102
104
|
"dev:build": "bun run docs:build",
|
|
103
105
|
"dev:generate": "bun run docs:generate",
|
|
104
106
|
"dev:preview": "bunx nuxt preview docs",
|
|
105
107
|
"dev:prepare": "bun run docs:prepare",
|
|
106
108
|
"docs:build": "nuxt build docs",
|
|
107
|
-
"docs:dev": "
|
|
109
|
+
"docs:dev": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare packages/docs/preview && nuxt dev packages/docs/preview",
|
|
108
110
|
"docs:generate": "nuxt generate docs",
|
|
109
111
|
"docs:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare docs && bun run scripts/sync-workspace-dist.mjs",
|
|
110
112
|
"docs:types": "cd docs && vue-tsc --noEmit",
|
|
111
|
-
"docs-template:dev": "bun run docs-template:prepare && nuxt dev packages/docs/preview",
|
|
112
|
-
"docs-template:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare packages/docs/preview",
|
|
113
113
|
"release": "bun run release:both",
|
|
114
114
|
"release:both": "bun run release:selaras && bun run release:docs",
|
|
115
115
|
"release:selaras": "bun run lint && bun run test && (if [ -n \"$RELEASE_VERSION\" ]; then npx changelogen --bump -r \"$RELEASE_VERSION\"; elif [ \"${RELEASE_BUMP:-auto}\" = \"auto\" ]; then npx changelogen --bump; else npx changelogen --bump \"--$RELEASE_BUMP\"; fi) && bun run test:release && npm publish --access public && bash scripts/tag-and-push-release.sh selaras && changelogen gh release",
|