srcdev-nuxt-components 9.1.54 → 9.1.56
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/.claude/settings.json +29 -2
- package/.claude/skills/components/alert-content-inner.md +60 -0
- package/.claude/skills/components/display-dialog.md +80 -0
- package/.claude/skills/components/display-prompt.md +163 -0
- package/.claude/skills/components/display-toast.md +235 -0
- package/.claude/skills/index.md +7 -3
- package/.claude/skills/theming-colour-ramps.md +328 -0
- package/.claude/skills/theming-override-default.md +115 -186
- package/.claude/skills/theming-partial-override.md +88 -75
- package/README.md +166 -0
- package/app/app.config.ts +44 -0
- package/app/assets/styles/setup/01.config/_head.css +3 -3
- package/app/assets/styles/setup/02.colours/_amber.css +12 -10
- package/app/assets/styles/setup/02.colours/_blue.css +10 -9
- package/app/assets/styles/setup/02.colours/_green.css +12 -11
- package/app/assets/styles/setup/02.colours/_orange.css +12 -10
- package/app/assets/styles/setup/02.colours/_red.css +10 -9
- package/app/assets/styles/setup/02.colours/_slate.css +12 -11
- package/app/assets/styles/setup/02.colours/_sunset.css +12 -10
- package/app/assets/styles/setup/02.colours/_theme-params.css +18 -0
- package/app/assets/styles/setup/02.colours/index.css +1 -0
- package/app/assets/styles/setup/03.theming/_default.css +87 -0
- package/app/assets/styles/setup/03.theming/_error.css +14 -0
- package/app/assets/styles/setup/03.theming/_success.css +4 -0
- package/app/assets/styles/setup/03.theming/_theme-slots.css +16 -0
- package/app/assets/styles/setup/03.theming/_warning.css +13 -0
- package/app/assets/styles/setup/03.theming/index.css +6 -4
- package/app/assets/styles/setup/03.theming/theme-ramp.css +19 -0
- package/app/assets/styles/setup/04.elements/forms/00.element-defaults.css +0 -7
- package/app/assets/styles/setup/04.elements/forms/06.button-geometry.css +1 -1
- package/app/assets/styles/setup/index.css +1 -1
- package/app/components/01.atoms/display-dialog/DisplayDialog.vue +74 -24
- package/app/components/01.atoms/display-dialog/tests/DisplayDialog.spec.ts +148 -14
- package/app/components/01.atoms/display-dialog/tests/__snapshots__/DisplayDialog.spec.ts.snap +3 -3
- package/app/components/01.atoms/page-row/PageRow.vue +5 -0
- package/app/components/01.atoms/prompt/DisplayPrompt.vue +113 -0
- package/app/components/01.atoms/prompt/stories/DisplayPrompt.stories.ts +112 -0
- package/app/components/01.atoms/prompt/tests/DisplayPrompt.spec.ts +205 -0
- package/app/components/01.atoms/toast/DisplayToast.vue +327 -0
- package/app/components/01.atoms/toast/DisplayToastProvider.vue +362 -0
- package/app/components/01.atoms/toast/stories/DisplayToast.stories.ts +195 -0
- package/app/components/01.atoms/toast/stories/DisplayToastProvider.stories.ts +142 -0
- package/app/components/01.atoms/toast/tests/DisplayToast.spec.ts +266 -0
- package/app/components/01.atoms/toast/tests/DisplayToastProvider.spec.ts +256 -0
- package/app/components/02.molecules/action-menu/ActionMenu.vue +8 -1
- package/app/components/02.molecules/action-menu/ActionMenuItemCore.vue +11 -3
- package/app/components/02.molecules/alert-content/AlertContent.vue +67 -0
- package/app/components/02.molecules/alert-content/AlertContentInner.vue +153 -0
- package/app/components/02.molecules/alert-content/stories/AlertContent.stories.ts +131 -0
- package/app/components/02.molecules/alert-content/tests/AlertContent.spec.ts +135 -0
- package/app/components/02.molecules/alert-content/tests/AlertContentInner.spec.ts +135 -0
- package/app/components/02.molecules/alert-masked-content/AlertMaskedContent.vue +78 -0
- package/app/components/02.molecules/alert-masked-content/stories/AlertMaskedContent.stories.ts +132 -0
- package/app/components/02.molecules/alert-masked-content/tests/AlertMaskedContent.spec.ts +149 -0
- package/app/components/05.forms/input-button/InputButtonCore.vue +35 -39
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioButton.vue +8 -8
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioCore.vue +5 -5
- package/app/components/05.forms/input-copy/InputCopyCore.vue +7 -7
- package/app/components/05.forms/input-number/InputNumberCore.vue +5 -5
- package/app/components/05.forms/input-select/InputSelectCore.vue +6 -6
- package/app/components/05.forms/input-text/InputTextCore.vue +6 -6
- package/app/components/05.forms/input-text/variants/InputTextAsNumberWithLabel.vue +2 -2
- package/app/components/05.forms/input-textarea/InputTextareaCore.vue +5 -5
- package/app/components/05.forms/pending-effect/PendingEffect.vue +60 -60
- package/app/components/05.forms/toggle-switch/ToggleSwitchCore.vue +10 -10
- package/app/components/05.forms/triple-toggle-switch/TripleToggleSwitchCore.vue +4 -4
- package/app/components/display-theme-switch/DisplayThemeSwitch.vue +4 -4
- package/app/components/display-tooltip/DisplayTooltip.vue +4 -4
- package/app/composables/useBodyLock.ts +21 -0
- package/app/composables/useDialogControls.ts +11 -11
- package/app/composables/useToastQueue.ts +39 -0
- package/app/layouts/default.vue +1 -1
- package/app/pages/ui/display-dialog.vue +275 -127
- package/app/pages/ui/display-prompt.vue +255 -52
- package/app/pages/ui/display-toast.vue +278 -299
- package/app/pages/ui/mask-element.vue +25 -3
- package/app/pages/ui/settings.vue +9 -35
- package/app/types/app-config.d.ts +41 -0
- package/app/types/components/display-prompt.d.ts +11 -0
- package/app/types/components/display-toast.d.ts +11 -9
- package/app/types/components/index.ts +2 -0
- package/app/types/components/semantic-theme.d.ts +1 -0
- package/nuxt.config.ts +16 -0
- package/package.json +6 -4
- package/app/assets/styles/setup/03.theming/default/_dark.css +0 -156
- package/app/assets/styles/setup/03.theming/default/_light.css +0 -166
- package/app/assets/styles/setup/03.theming/default/index.css +0 -2
- package/app/assets/styles/setup/03.theming/error/_dark.css +0 -56
- package/app/assets/styles/setup/03.theming/error/_light.css +0 -74
- package/app/assets/styles/setup/03.theming/error/index.css +0 -2
- package/app/assets/styles/setup/03.theming/success/_dark.css +0 -50
- package/app/assets/styles/setup/03.theming/success/_light.css +0 -65
- package/app/assets/styles/setup/03.theming/success/index.css +0 -2
- package/app/assets/styles/setup/03.theming/warning/_dark.css +0 -50
- package/app/assets/styles/setup/03.theming/warning/_light.css +0 -64
- package/app/assets/styles/setup/03.theming/warning/index.css +0 -2
- package/app/components/display-prompt/DisplayPromptCore.vue +0 -228
- package/app/components/display-prompt/variants/DisplayPromptError.vue +0 -53
- package/app/components/display-toast/DisplayToast.vue +0 -447
- package/app/components/display-toast/molecules/DefaultToastContent.vue +0 -163
- package/app/components/display-toast/stories/DisplayToast.stories.ts +0 -380
package/app/components/02.molecules/alert-masked-content/stories/AlertMaskedContent.stories.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@nuxtjs/storybook";
|
|
2
|
+
import AlertMaskedContent from "../AlertMaskedContent.vue";
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof AlertMaskedContent> = {
|
|
5
|
+
title: "Molecules/AlertMaskedContent",
|
|
6
|
+
component: AlertMaskedContent,
|
|
7
|
+
argTypes: {
|
|
8
|
+
theme: {
|
|
9
|
+
control: { type: "inline-radio" },
|
|
10
|
+
options: ["info", "success", "warning", "error"],
|
|
11
|
+
description: "Semantic theme — controls the accent colour and default icon",
|
|
12
|
+
table: { category: "Appearance" },
|
|
13
|
+
},
|
|
14
|
+
customIcon: {
|
|
15
|
+
control: "text",
|
|
16
|
+
description: "Iconify icon name to override the default theme icon",
|
|
17
|
+
table: { category: "Appearance" },
|
|
18
|
+
},
|
|
19
|
+
dismissible: {
|
|
20
|
+
control: "boolean",
|
|
21
|
+
description: "Show a close button (emits 'dismiss' on click)",
|
|
22
|
+
table: { category: "Behaviour" },
|
|
23
|
+
},
|
|
24
|
+
maskConfig: { table: { disable: true } },
|
|
25
|
+
contentId: { table: { disable: true } },
|
|
26
|
+
ariaLive: { table: { disable: true } },
|
|
27
|
+
},
|
|
28
|
+
args: {
|
|
29
|
+
theme: "info",
|
|
30
|
+
dismissible: false,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default meta;
|
|
35
|
+
type Story = StoryObj<typeof AlertMaskedContent>;
|
|
36
|
+
|
|
37
|
+
const wrapperStyle = `
|
|
38
|
+
max-width: 600px;
|
|
39
|
+
padding: 2rem;
|
|
40
|
+
background: linear-gradient(135deg, oklch(40% 0.15 250), oklch(25% 0.1 300));
|
|
41
|
+
border-radius: 8px;
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
/** Default — info theme over a coloured background to show the glass effect. */
|
|
45
|
+
export const Default: Story = {
|
|
46
|
+
render: (args) => ({
|
|
47
|
+
components: { AlertMaskedContent },
|
|
48
|
+
setup() {
|
|
49
|
+
return { args };
|
|
50
|
+
},
|
|
51
|
+
template: `
|
|
52
|
+
<div :data-theme="args.theme" :style="'${wrapperStyle}'">
|
|
53
|
+
<AlertMaskedContent v-bind="args">
|
|
54
|
+
<template #title>Alert title</template>
|
|
55
|
+
<template #content>This is the alert body. It describes what happened and what the user should do next.</template>
|
|
56
|
+
</AlertMaskedContent>
|
|
57
|
+
</div>
|
|
58
|
+
`,
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** Dismissible — renders the close button. */
|
|
63
|
+
export const Dismissible: Story = {
|
|
64
|
+
args: { theme: "info", dismissible: true },
|
|
65
|
+
render: (args) => ({
|
|
66
|
+
components: { AlertMaskedContent },
|
|
67
|
+
setup() {
|
|
68
|
+
return { args };
|
|
69
|
+
},
|
|
70
|
+
template: `
|
|
71
|
+
<div :data-theme="args.theme" :style="'${wrapperStyle}'">
|
|
72
|
+
<AlertMaskedContent v-bind="args">
|
|
73
|
+
<template #title>Dismissible alert</template>
|
|
74
|
+
<template #content>Click the close button to emit the dismiss event.</template>
|
|
75
|
+
</AlertMaskedContent>
|
|
76
|
+
</div>
|
|
77
|
+
`,
|
|
78
|
+
}),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** All themes — all four semantic variants stacked over a shared background. */
|
|
82
|
+
export const AllThemes: Story = {
|
|
83
|
+
name: "All Themes",
|
|
84
|
+
render: () => ({
|
|
85
|
+
components: { AlertMaskedContent },
|
|
86
|
+
setup() {
|
|
87
|
+
return { themes: ["info", "success", "warning", "error"] as const };
|
|
88
|
+
},
|
|
89
|
+
template: `
|
|
90
|
+
<div style="${wrapperStyle} display: flex; flex-direction: column; gap: 1.2rem;">
|
|
91
|
+
<div v-for="theme in themes" :key="theme" :data-theme="theme">
|
|
92
|
+
<AlertMaskedContent :theme="theme" :dismissible="true">
|
|
93
|
+
<template #title>{{ theme.charAt(0).toUpperCase() + theme.slice(1) }}</template>
|
|
94
|
+
<template #content>This is the {{ theme }} masked variant.</template>
|
|
95
|
+
</AlertMaskedContent>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
`,
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/** Compared side-by-side with different background colours to show how the glass adapts. */
|
|
103
|
+
export const BackgroundVariants: Story = {
|
|
104
|
+
name: "Background Variants",
|
|
105
|
+
render: () => ({
|
|
106
|
+
components: { AlertMaskedContent },
|
|
107
|
+
setup() {
|
|
108
|
+
return {
|
|
109
|
+
backgrounds: [
|
|
110
|
+
"linear-gradient(135deg, oklch(40% 0.15 250), oklch(25% 0.1 300))",
|
|
111
|
+
"linear-gradient(135deg, oklch(35% 0.12 30), oklch(20% 0.08 60))",
|
|
112
|
+
"linear-gradient(135deg, oklch(20% 0 0), oklch(10% 0 0))",
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
template: `
|
|
117
|
+
<div style="display: flex; flex-direction: column; gap: 1.6rem;">
|
|
118
|
+
<div
|
|
119
|
+
v-for="(bg, i) in backgrounds"
|
|
120
|
+
:key="i"
|
|
121
|
+
:style="{ background: bg, padding: '2rem', borderRadius: '8px', maxWidth: '500px' }"
|
|
122
|
+
data-theme="info"
|
|
123
|
+
>
|
|
124
|
+
<AlertMaskedContent theme="info" :dismissible="true">
|
|
125
|
+
<template #title>Glass effect</template>
|
|
126
|
+
<template #content>The masked border adapts to whatever sits behind it.</template>
|
|
127
|
+
</AlertMaskedContent>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
`,
|
|
131
|
+
}),
|
|
132
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { mountSuspended } from "@nuxt/test-utils/runtime";
|
|
3
|
+
import AlertMaskedContent from "../AlertMaskedContent.vue";
|
|
4
|
+
|
|
5
|
+
const mockResizeObserver = vi.fn(() => ({
|
|
6
|
+
observe: vi.fn(),
|
|
7
|
+
unobserve: vi.fn(),
|
|
8
|
+
disconnect: vi.fn(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
vi.stubGlobal("ResizeObserver", mockResizeObserver);
|
|
12
|
+
|
|
13
|
+
describe("AlertMaskedContent", () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
vi.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// ─── Mount ────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
it("mounts without error", async () => {
|
|
21
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info" } });
|
|
22
|
+
expect(wrapper.vm).toBeTruthy();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// ─── Root element ─────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
it("renders the root element with class alert-masked-content", async () => {
|
|
28
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info" } });
|
|
29
|
+
expect(wrapper.find(".alert-masked-content").exists()).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("sets data-theme on the root element", async () => {
|
|
33
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "success" } });
|
|
34
|
+
expect(wrapper.find(".alert-masked-content").attributes("data-theme")).toBe("success");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// ─── AlertMaskCore ────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
it("renders AlertMaskCore", async () => {
|
|
40
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info" } });
|
|
41
|
+
expect(wrapper.find(".alert-mask-core").exists()).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("renders AlertContentInner inside AlertMaskCore", async () => {
|
|
45
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info" } });
|
|
46
|
+
expect(wrapper.find(".alert-mask-core .alert-content-inner").exists()).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// ─── Icon ─────────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
it("renders the icon region", async () => {
|
|
52
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info" } });
|
|
53
|
+
expect(wrapper.find("[data-test-id='alert-icon']").exists()).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("replaces the icon via the #icon slot", async () => {
|
|
57
|
+
const wrapper = await mountSuspended(AlertMaskedContent, {
|
|
58
|
+
props: { theme: "info" },
|
|
59
|
+
slots: { icon: '<span class="custom-icon">★</span>' },
|
|
60
|
+
});
|
|
61
|
+
expect(wrapper.find(".custom-icon").exists()).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// ─── Title slot ───────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
it("renders the title element when the #title slot is provided", async () => {
|
|
67
|
+
const wrapper = await mountSuspended(AlertMaskedContent, {
|
|
68
|
+
props: { theme: "info" },
|
|
69
|
+
slots: { title: "Important notice" },
|
|
70
|
+
});
|
|
71
|
+
expect(wrapper.find("[data-test-id='alert-title']").text()).toContain("Important notice");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("does not render the title element when the #title slot is not provided", async () => {
|
|
75
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info" } });
|
|
76
|
+
expect(wrapper.find("[data-test-id='alert-title']").exists()).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// ─── Content slot ─────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
it("renders the content element when the #content slot is provided", async () => {
|
|
82
|
+
const wrapper = await mountSuspended(AlertMaskedContent, {
|
|
83
|
+
props: { theme: "info" },
|
|
84
|
+
slots: { content: "More detail here." },
|
|
85
|
+
});
|
|
86
|
+
expect(wrapper.find("[data-test-id='alert-content']").text()).toContain("More detail here.");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("does not render the content element when the #content slot is not provided", async () => {
|
|
90
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info" } });
|
|
91
|
+
expect(wrapper.find("[data-test-id='alert-content']").exists()).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// ─── contentId / ariaLive ─────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
it("sets id on the body element when contentId is provided", async () => {
|
|
97
|
+
const wrapper = await mountSuspended(AlertMaskedContent, {
|
|
98
|
+
props: { theme: "info", contentId: "toast-message-xyz" },
|
|
99
|
+
});
|
|
100
|
+
expect(wrapper.find(".alert-content-body").attributes("id")).toBe("toast-message-xyz");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("sets aria-live on the body element when ariaLive is provided", async () => {
|
|
104
|
+
const wrapper = await mountSuspended(AlertMaskedContent, {
|
|
105
|
+
props: { theme: "info", ariaLive: "assertive" },
|
|
106
|
+
});
|
|
107
|
+
expect(wrapper.find(".alert-content-body").attributes("aria-live")).toBe("assertive");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ─── Dismiss button ───────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
it("does not render the dismiss button when dismissible is false", async () => {
|
|
113
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info", dismissible: false } });
|
|
114
|
+
expect(wrapper.find("[data-test-id='alert-dismiss']").exists()).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("renders the dismiss button when dismissible is true", async () => {
|
|
118
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info", dismissible: true } });
|
|
119
|
+
expect(wrapper.find("[data-test-id='alert-dismiss']").exists()).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("emits the dismiss event when the dismiss button is clicked", async () => {
|
|
123
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info", dismissible: true } });
|
|
124
|
+
await wrapper.find("[data-test-id='alert-dismiss']").trigger("click");
|
|
125
|
+
expect(wrapper.emitted("dismiss")).toBeTruthy();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// ─── Default maskConfig ───────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
it("passes the resolved maskConfig to AlertMaskCore", async () => {
|
|
131
|
+
const wrapper = await mountSuspended(AlertMaskedContent, { props: { theme: "info" } });
|
|
132
|
+
const contentEl = wrapper.find(".alert-mask-content");
|
|
133
|
+
const style = (contentEl.element as HTMLElement).style;
|
|
134
|
+
// Default borderLeft: 6 → --insetInlineStart: 6px
|
|
135
|
+
expect(style.getPropertyValue("--insetInlineStart")).toBe("6px");
|
|
136
|
+
expect(style.getPropertyValue("--insetBlockStart")).toBe("1px");
|
|
137
|
+
expect(style.getPropertyValue("--insetInlineEnd")).toBe("1px");
|
|
138
|
+
expect(style.getPropertyValue("--insetBlockEnd")).toBe("1px");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("merges custom maskConfig over defaults", async () => {
|
|
142
|
+
const wrapper = await mountSuspended(AlertMaskedContent, {
|
|
143
|
+
props: { theme: "info", maskConfig: { borderLeft: 12 } },
|
|
144
|
+
});
|
|
145
|
+
const contentEl = wrapper.find(".alert-mask-content");
|
|
146
|
+
const style = (contentEl.element as HTMLElement).style;
|
|
147
|
+
expect(style.getPropertyValue("--insetInlineStart")).toBe("12px");
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
class="input-button-core"
|
|
11
11
|
:class="buttonClasses"
|
|
12
12
|
>
|
|
13
|
-
<PendingEffect v-if="hasPendingEffect" />
|
|
13
|
+
<PendingEffect v-if="hasPendingEffect" :theme="theme" />
|
|
14
14
|
|
|
15
15
|
<span v-if="hasLeftSlot" class="btn-icon left">
|
|
16
16
|
<slot name="left"></slot>
|
|
@@ -87,7 +87,7 @@ const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
|
87
87
|
<style lang="css">
|
|
88
88
|
@layer components {
|
|
89
89
|
.input-button-core {
|
|
90
|
-
all: unset;
|
|
90
|
+
/* all: unset; */
|
|
91
91
|
|
|
92
92
|
display: grid;
|
|
93
93
|
grid-auto-flow: column;
|
|
@@ -108,53 +108,52 @@ const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/*
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
111
|
+
* Theming
|
|
112
|
+
**/
|
|
113
|
+
background-color: var(--theme-surface);
|
|
114
|
+
color: var(--theme-on-surface);
|
|
115
|
+
border: var(--button-border-width) solid var(--theme-border);
|
|
116
|
+
outline: var(--button-outline-width) solid var(--theme-ring);
|
|
117
|
+
|
|
118
|
+
&:hover,
|
|
119
|
+
&:focus-visible {
|
|
120
|
+
background-color: var(--theme-surface-hover);
|
|
121
|
+
color: var(--theme-text);
|
|
122
|
+
border-color: var(--theme-border-focus);
|
|
123
|
+
outline-color: var(--theme-ring);
|
|
124
|
+
}
|
|
126
125
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
126
|
+
&.is-pending {
|
|
127
|
+
background-color: color-mix(in oklab, var(--theme-surface) 50%, transparent);
|
|
130
128
|
}
|
|
131
129
|
|
|
132
130
|
&.secondary {
|
|
133
|
-
background-color: var(--theme-
|
|
134
|
-
border: var(--button-border-width) solid var(--theme-
|
|
135
|
-
color: var(--theme-
|
|
136
|
-
outline: var(--button-outline-width) solid var(--theme-
|
|
131
|
+
background-color: var(--theme-surface-inverted);
|
|
132
|
+
border: var(--button-border-width) solid var(--theme-border);
|
|
133
|
+
color: var(--theme-text-inverted);
|
|
134
|
+
outline: var(--button-outline-width) solid var(--theme-ring);
|
|
137
135
|
|
|
138
136
|
&:hover,
|
|
139
137
|
&:focus-visible {
|
|
140
|
-
background-color: var(--theme-
|
|
141
|
-
color: var(--theme-
|
|
142
|
-
border-color: var(--theme-
|
|
143
|
-
outline-color: var(--theme-
|
|
138
|
+
background-color: var(--theme-surface);
|
|
139
|
+
color: var(--theme-on-surface);
|
|
140
|
+
border-color: var(--theme-border-focus);
|
|
141
|
+
outline-color: var(--theme-surface);
|
|
144
142
|
}
|
|
145
143
|
}
|
|
146
144
|
|
|
147
145
|
&.tertiary {
|
|
148
|
-
background-color: var(--
|
|
149
|
-
border: var(--button-border-width) solid
|
|
150
|
-
color: var(--theme-
|
|
146
|
+
background-color: light-dark(var(--slate-01), transparent);
|
|
147
|
+
border: var(--button-border-width) solid var(--theme-border);
|
|
148
|
+
color: var(--theme-text);
|
|
151
149
|
text-decoration: underline;
|
|
152
|
-
outline: var(--button-outline-width) solid
|
|
150
|
+
outline: var(--button-outline-width) solid var(--theme-border-focus);
|
|
153
151
|
|
|
154
152
|
&:hover,
|
|
155
153
|
&:focus-visible {
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
background-color: var(--theme-surface-hover);
|
|
155
|
+
border-color: var(--theme-border-focus);
|
|
156
|
+
outline-color: var(--theme-border-focus);
|
|
158
157
|
}
|
|
159
158
|
}
|
|
160
159
|
|
|
@@ -180,11 +179,8 @@ const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
|
180
179
|
|
|
181
180
|
&[readonly] {
|
|
182
181
|
opacity: 0.5;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
cursor: not-allowed;
|
|
186
|
-
pointer-events: none;
|
|
187
|
-
}
|
|
182
|
+
cursor: not-allowed;
|
|
183
|
+
pointer-events: none;
|
|
188
184
|
}
|
|
189
185
|
|
|
190
186
|
.button-text {
|
|
@@ -100,22 +100,22 @@ const flexDirection = ref(props.direction);
|
|
|
100
100
|
--_white-space: nowrap;
|
|
101
101
|
}
|
|
102
102
|
background-color: var(--theme-input-surface);
|
|
103
|
-
border: var(--form-element-border-width) solid var(--theme-
|
|
104
|
-
outline: var(--form-element-outline-width) solid
|
|
103
|
+
border: var(--form-element-border-width) solid var(--theme-border);
|
|
104
|
+
outline: var(--form-element-outline-width) solid transparent;
|
|
105
105
|
|
|
106
106
|
box-shadow: 0.1rem 0.1rem 0.8rem 0.1rem transparent;
|
|
107
107
|
|
|
108
108
|
&:hover {
|
|
109
109
|
background-color: var(--theme-input-surface-hover);
|
|
110
|
-
border-color: var(--theme-
|
|
111
|
-
outline-color: var(--theme-
|
|
110
|
+
border-color: var(--theme-border);
|
|
111
|
+
outline-color: var(--theme-ring);
|
|
112
112
|
outline-offset: var(--form-element-outline-offset-focus);
|
|
113
113
|
cursor: pointer;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
&:has(.input-checkbox-radio-core:focus-visible) {
|
|
117
|
-
background-color: var(--theme-
|
|
118
|
-
outline-color: var(--theme-
|
|
117
|
+
background-color: var(--theme-surface-subtle);
|
|
118
|
+
outline-color: var(--theme-border-focus);
|
|
119
119
|
outline-offset: var(--form-element-outline-offset-focus);
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -126,7 +126,7 @@ const flexDirection = ref(props.direction);
|
|
|
126
126
|
.input-checkbox-radio-options-button-label {
|
|
127
127
|
display: flex;
|
|
128
128
|
flex-grow: 1;
|
|
129
|
-
color: var(--
|
|
129
|
+
color: var(--colour-text-default);
|
|
130
130
|
font-size: var(--input-font-size);
|
|
131
131
|
width: 100%;
|
|
132
132
|
min-height: var(--input-element-line-height);
|
|
@@ -145,7 +145,7 @@ const flexDirection = ref(props.direction);
|
|
|
145
145
|
display: flex;
|
|
146
146
|
align-items: center;
|
|
147
147
|
justify-content: center;
|
|
148
|
-
color: var(--theme-
|
|
148
|
+
color: var(--theme-text);
|
|
149
149
|
|
|
150
150
|
.icon {
|
|
151
151
|
height: var(--input-checkbox-decorator-icon-size);
|
|
@@ -113,8 +113,8 @@ const inputClasses = computed(() => [
|
|
|
113
113
|
place-content: center;
|
|
114
114
|
|
|
115
115
|
background-color: var(--theme-checkbox-symbol-surface);
|
|
116
|
-
border: 0.1rem solid var(--theme-
|
|
117
|
-
outline: var(--form-element-outline-width) solid
|
|
116
|
+
border: 0.1rem solid var(--theme-border);
|
|
117
|
+
outline: var(--form-element-outline-width) solid transparent;
|
|
118
118
|
|
|
119
119
|
height: var(--input-checked-element-size);
|
|
120
120
|
width: var(--input-checked-element-size);
|
|
@@ -139,7 +139,7 @@ const inputClasses = computed(() => [
|
|
|
139
139
|
|
|
140
140
|
&:not(:is(.button)) {
|
|
141
141
|
&:has(input:focus-visible) {
|
|
142
|
-
outline: var(--form-element-outline-width-focus) solid var(--theme-
|
|
142
|
+
outline: var(--form-element-outline-width-focus) solid var(--theme-border-focus);
|
|
143
143
|
outline-offset: var(--form-element-outline-offset-focus);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
@@ -150,7 +150,7 @@ const inputClasses = computed(() => [
|
|
|
150
150
|
|
|
151
151
|
.input-checked-icon-checked,
|
|
152
152
|
.icon {
|
|
153
|
-
color: var(--theme-
|
|
153
|
+
color: var(--theme-text);
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
}
|
|
@@ -164,7 +164,7 @@ const inputClasses = computed(() => [
|
|
|
164
164
|
|
|
165
165
|
.input-checked-icon-checked,
|
|
166
166
|
.icon {
|
|
167
|
-
color: var(--theme-
|
|
167
|
+
color: var(--theme-text);
|
|
168
168
|
height: var(--input-checked-icon-size);
|
|
169
169
|
width: var(--input-checked-icon-size);
|
|
170
170
|
box-shadow: var(--_box-shadow);
|
|
@@ -79,7 +79,7 @@ watch(
|
|
|
79
79
|
display: flex;
|
|
80
80
|
align-items: stretch;
|
|
81
81
|
overflow: hidden;
|
|
82
|
-
border: var(--form-element-border-width) solid var(--theme-
|
|
82
|
+
border: var(--form-element-border-width) solid var(--theme-border);
|
|
83
83
|
border-radius: var(--form-input-border-radius);
|
|
84
84
|
background-color: var(--theme-input-surface);
|
|
85
85
|
transition: all var(--theme-form-transition-duration) ease-in-out;
|
|
@@ -101,19 +101,19 @@ watch(
|
|
|
101
101
|
|
|
102
102
|
.input-copy-button.input-button-core {
|
|
103
103
|
border-radius: 0;
|
|
104
|
-
border-inline-start: var(--form-element-border-width) solid var(--theme-
|
|
104
|
+
border-inline-start: var(--form-element-border-width) solid var(--theme-border);
|
|
105
105
|
padding-inline: var(--input-padding-inline);
|
|
106
106
|
min-width: var(--input-min-height);
|
|
107
|
-
background-color:
|
|
108
|
-
color: var(--theme-
|
|
107
|
+
background-color: transparent;
|
|
108
|
+
color: var(--theme-text);
|
|
109
109
|
|
|
110
110
|
&:hover {
|
|
111
|
-
background-color: var(--theme-
|
|
112
|
-
color: var(--theme-
|
|
111
|
+
background-color: var(--theme-surface);
|
|
112
|
+
color: var(--theme-on-surface);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
&:focus-visible {
|
|
116
|
-
outline: var(--form-element-outline-width-focus) solid var(--theme-
|
|
116
|
+
outline: var(--form-element-outline-width-focus) solid var(--theme-border-focus);
|
|
117
117
|
outline-offset: -4px;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -71,10 +71,10 @@ const minLength = computed(() => `${props.max.toString().length + 1}em`);
|
|
|
71
71
|
|
|
72
72
|
width: fit-content;
|
|
73
73
|
|
|
74
|
-
background-color: var(--theme-
|
|
74
|
+
background-color: var(--theme-input-surface);
|
|
75
75
|
border-radius: var(--form-element-border-width);
|
|
76
|
-
border: var(--form-element-border-width) solid var(--theme-
|
|
77
|
-
outline: var(--form-element-outline-width) solid
|
|
76
|
+
border: var(--form-element-border-width) solid var(--theme-border);
|
|
77
|
+
outline: var(--form-element-outline-width) solid transparent;
|
|
78
78
|
box-shadow: var(--_focus-box-shadow);
|
|
79
79
|
|
|
80
80
|
.slot {
|
|
@@ -144,12 +144,12 @@ const minLength = computed(() => `${props.max.toString().length + 1}em`);
|
|
|
144
144
|
|
|
145
145
|
.left-slot {
|
|
146
146
|
margin-inline-end: 0;
|
|
147
|
-
border-right: 2px solid var(--theme-
|
|
147
|
+
border-right: 2px solid var(--theme-input-surface-hover);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
.right-slot {
|
|
151
151
|
margin-inline-end: 0;
|
|
152
|
-
border-left: 2px solid var(--theme-
|
|
152
|
+
border-left: 2px solid var(--theme-input-surface-hover);
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
}
|
|
@@ -81,18 +81,18 @@ const fieldData = defineModel("fieldData") as Ref<IFormMultipleOptions>;
|
|
|
81
81
|
transition: all var(--theme-form-transition-duration) ease-in-out;
|
|
82
82
|
|
|
83
83
|
&.normal {
|
|
84
|
-
border: var(--form-element-border-width) solid var(--theme-
|
|
84
|
+
border: var(--form-element-border-width) solid var(--theme-border);
|
|
85
85
|
border-radius: var(--form-input-border-radius);
|
|
86
|
-
outline: var(--form-element-outline-width) solid
|
|
86
|
+
outline: var(--form-element-outline-width) solid transparent;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
&.underlined {
|
|
90
|
-
border-bottom: var(--form-element-border-bottom-width-underlined) solid var(--theme-
|
|
90
|
+
border-bottom: var(--form-element-border-bottom-width-underlined) solid var(--theme-border);
|
|
91
91
|
background-color: var(--theme-input-surface);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
&:has(select:focus-visible, :hover) {
|
|
95
|
-
outline: var(--form-element-outline-width-focus) solid var(--theme-
|
|
95
|
+
outline: var(--form-element-outline-width-focus) solid var(--theme-border-focus);
|
|
96
96
|
outline-offset: var(--form-element-outline-offset-focus);
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -105,7 +105,7 @@ const fieldData = defineModel("fieldData") as Ref<IFormMultipleOptions>;
|
|
|
105
105
|
content: '';
|
|
106
106
|
width: 0.8em;
|
|
107
107
|
height: 0.5em;
|
|
108
|
-
background-color: var(--theme-
|
|
108
|
+
background-color: var(--theme-border);
|
|
109
109
|
clip-path: polygon(100% 0%, 0 0%, 50% 100%);
|
|
110
110
|
} */
|
|
111
111
|
|
|
@@ -128,7 +128,7 @@ const fieldData = defineModel("fieldData") as Ref<IFormMultipleOptions>;
|
|
|
128
128
|
|
|
129
129
|
&:open::picker(select) {
|
|
130
130
|
opacity: 1;
|
|
131
|
-
border: var(--form-element-border-width) solid var(--theme-
|
|
131
|
+
border: var(--form-element-border-width) solid var(--theme-border);
|
|
132
132
|
outline: var(--form-element-outline-width) solid var(--_input-select-outline-color);
|
|
133
133
|
|
|
134
134
|
@starting-style {
|
|
@@ -138,25 +138,25 @@ onMounted(() => {
|
|
|
138
138
|
position: relative;
|
|
139
139
|
|
|
140
140
|
&.normal {
|
|
141
|
-
border: var(--form-element-border-width) solid var(--theme-
|
|
141
|
+
border: var(--form-element-border-width) solid var(--theme-border);
|
|
142
142
|
border-radius: var(--form-input-border-radius);
|
|
143
|
-
outline: var(--form-element-outline-width) solid
|
|
143
|
+
outline: var(--form-element-outline-width) solid transparent;
|
|
144
144
|
|
|
145
145
|
padding-inline: var(--input-padding-inline);
|
|
146
146
|
|
|
147
147
|
&:has(input:is(:hover), button:is(:hover)) {
|
|
148
|
-
outline: var(--form-element-outline-width-focus) solid var(--theme-
|
|
148
|
+
outline: var(--form-element-outline-width-focus) solid var(--theme-border-focus);
|
|
149
149
|
outline-offset: var(--form-element-outline-offset-focus);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
&:has(input:focus-visible, button:focus-visible) {
|
|
153
|
-
outline: var(--form-element-outline-width-focus) solid var(--theme-
|
|
153
|
+
outline: var(--form-element-outline-width-focus) solid var(--theme-border-focus);
|
|
154
154
|
outline-offset: var(--form-element-outline-offset-focus);
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
&.underlined {
|
|
159
|
-
border-bottom: var(--form-element-border-bottom-width-underlined) solid var(--theme-
|
|
159
|
+
border-bottom: var(--form-element-border-bottom-width-underlined) solid var(--theme-border);
|
|
160
160
|
padding-inline: var(--input-padding-inline);
|
|
161
161
|
}
|
|
162
162
|
|
|
@@ -181,7 +181,7 @@ onMounted(() => {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
&:is(:focus-visible) {
|
|
184
|
-
outline: var(--form-element-outline-width-focus) solid var(--theme-
|
|
184
|
+
outline: var(--form-element-outline-width-focus) solid var(--theme-border-focus);
|
|
185
185
|
outline-offset: -4px;
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -180,13 +180,13 @@ updateElementClasses(["input-text-as-number", "has-left-button", "has-right-butt
|
|
|
180
180
|
|
|
181
181
|
&.left-slot {
|
|
182
182
|
.input-button-core {
|
|
183
|
-
border-right: var(--form-element-border-width) solid var(--theme-
|
|
183
|
+
border-right: var(--form-element-border-width) solid var(--theme-border);
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
&.right-slot {
|
|
188
188
|
.input-button-core {
|
|
189
|
-
border-left: var(--form-element-border-width) solid var(--theme-
|
|
189
|
+
border-left: var(--form-element-border-width) solid var(--theme-border);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
}
|