@filipgutica/ui 1.1.0 → 1.2.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/README.md +50 -163
- package/dist/components/UiDrawer.vue.d.ts +23 -0
- package/dist/components/UiTabs.vue.d.ts +27 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +262 -122
- package/dist/use-active-section.d.ts +7 -0
- package/package.json +2 -1
- package/src/components/drawer.css +100 -0
- package/src/components/tabs.css +52 -0
- package/src/components.css +6 -0
package/README.md
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# @filipgutica/ui
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Tailwind CSS 4
|
|
3
|
+
Compact Vue 3.5+ components built on [Reka UI](https://reka-ui.com), with
|
|
4
|
+
Tailwind CSS 4 and VS Code theme support.
|
|
5
|
+
|
|
6
|
+
[Docs and playground](https://filipgutica.github.io/ui/) ·
|
|
7
|
+
[GitHub](https://github.com/filipgutica/ui) ·
|
|
8
|
+
[npm](https://www.npmjs.com/package/@filipgutica/ui)
|
|
5
9
|
|
|
6
10
|
## Install
|
|
7
11
|
|
|
@@ -9,7 +13,7 @@ Tailwind CSS 4 tokens and safe VS Code/Open VSX color-theme normalization.
|
|
|
9
13
|
pnpm add @filipgutica/ui
|
|
10
14
|
```
|
|
11
15
|
|
|
12
|
-
Import
|
|
16
|
+
Import the styles from your application stylesheet:
|
|
13
17
|
|
|
14
18
|
```css
|
|
15
19
|
@import "tailwindcss";
|
|
@@ -17,193 +21,76 @@ Import Tailwind and the shared theme files from your application stylesheet:
|
|
|
17
21
|
@import "@filipgutica/ui/components.css";
|
|
18
22
|
```
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
`text-ui-text`, `text-ui-muted`, `border-ui-border`, and `text-ui-error`.
|
|
22
|
-
|
|
23
|
-
## Vue components
|
|
24
|
-
|
|
25
|
-
The first application-focused set includes:
|
|
26
|
-
|
|
27
|
-
- `UiButton` with primary, secondary, ghost, danger, and text variants
|
|
28
|
-
- `UiInput`, `UiSelect`, and `UiField` for consistent form controls
|
|
29
|
-
- `UiCheckbox`, `UiSlider`, `UiRadioCard`, and `UiRadioCardGroup` for user input
|
|
30
|
-
- `UiBadge`, `UiAlert`, and `UiProgress` for state and feedback
|
|
31
|
-
- `UiCard` for structured content, actions, and footer metadata
|
|
32
|
-
- `UiCodeBlock` for copyable, theme-aware source snippets
|
|
33
|
-
- `UiDialog`, built on Reka UI, for accessible modal interaction
|
|
34
|
-
|
|
35
|
-
Place `UiInput` or `UiSelect` inside `UiField`. The field supplies the control ID
|
|
36
|
-
and automatically connects its description or error through the corresponding
|
|
37
|
-
ARIA relationship.
|
|
24
|
+
## Use
|
|
38
25
|
|
|
39
26
|
```vue
|
|
27
|
+
<script setup lang="ts">
|
|
28
|
+
import { UiButton } from '@filipgutica/ui'
|
|
29
|
+
</script>
|
|
30
|
+
|
|
40
31
|
<template>
|
|
41
32
|
<UiButton variant="secondary" size="sm">
|
|
42
|
-
|
|
33
|
+
Save changes
|
|
43
34
|
</UiButton>
|
|
44
35
|
</template>
|
|
45
|
-
|
|
46
|
-
<script setup lang="ts">
|
|
47
|
-
import { UiButton } from '@filipgutica/ui'
|
|
48
|
-
</script>
|
|
49
36
|
```
|
|
50
37
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
The [component docs](https://filipgutica.github.io/ui/#/components/button)
|
|
39
|
+
cover examples, props, slots, and events. Place inputs and selects inside
|
|
40
|
+
`UiField` to connect labels, descriptions, and errors automatically.
|
|
54
41
|
|
|
55
|
-
|
|
56
|
-
`size="sm | md | lg"`
|
|
57
|
-
and default to the tighter `md` density. Existing `UiButton` values remain
|
|
58
|
-
compatible: `compact` retains its former compact treatment and `default` maps
|
|
59
|
-
to `lg`.
|
|
42
|
+
## Page navigation
|
|
60
43
|
|
|
61
|
-
`
|
|
62
|
-
|
|
63
|
-
|
|
44
|
+
`UiDrawer` provides modal focus containment, Escape dismissal, and trigger focus
|
|
45
|
+
restoration. Keep native navigation in the server-rendered page until hydration;
|
|
46
|
+
the application decides when to replace it and when to close the drawer at a
|
|
47
|
+
desktop breakpoint. To focus a link destination after closing, prevent the
|
|
48
|
+
cancelable `closeAutoFocus` event and focus that heading.
|
|
64
49
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
:line-numbers="true"
|
|
71
|
-
/>
|
|
72
|
-
```
|
|
50
|
+
`UiTabs` renders every labelled panel during SSR and initial hydration. After
|
|
51
|
+
mounting, it adds keyboard-accessible tabs and hides inactive panels without
|
|
52
|
+
unmounting them. Use unique item values and keep page anchor headings outside
|
|
53
|
+
the panels. Re-align an initial hash after layout settles if enhancement changes
|
|
54
|
+
the page height.
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
JSON, and Bash (`sh`/`shell`). Imported TextMate `tokenColors` are normalized
|
|
76
|
-
into a shared syntax palette; language-specific overrides and font styles are
|
|
77
|
-
not reproduced exactly. Themes saved before syntax support should be re-imported
|
|
78
|
-
to recover their syntax colors. Existing saved themes remain usable with themed fallbacks.
|
|
79
|
-
|
|
80
|
-
`UiSurface` remains exported for compatibility. New structured containers
|
|
81
|
-
should use `UiCard`.
|
|
82
|
-
|
|
83
|
-
## Design conventions
|
|
84
|
-
|
|
85
|
-
Use a compact system-font hierarchy: 14px for UI body text, 13px for
|
|
86
|
-
secondary text, and 18px for dialog titles. Treat Open VSX/VS Code themes as
|
|
87
|
-
the color authority, and use semantic tokens and package components instead of
|
|
88
|
-
hard-coded provider color keys.
|
|
89
|
-
Typography is package-owned through `--font-ui` and `--font-mono`; override
|
|
90
|
-
these once at the application root rather than restyling each component.
|
|
91
|
-
|
|
92
|
-
- Use shared control sizes `sm`, `md`, and `lg` (32px, 36px, and 44px); the
|
|
93
|
-
default is `md`. Keep layout on a 4px rhythm, with 12px, 16px, and 24px as
|
|
94
|
-
common gaps and insets.
|
|
95
|
-
- Choose `UiCard` for structured content and optional actions or footer data.
|
|
96
|
-
Choose `UiRadioCardGroup` with `UiRadioCard` for one-of-many rich options;
|
|
97
|
-
choose `UiDialog` for modal tasks that need focus management.
|
|
98
|
-
- Use one primary action per task, secondary buttons for supporting actions,
|
|
99
|
-
and danger styling only for destructive actions. Use `UiBadge` for a short
|
|
100
|
-
status, `UiAlert` for feedback, and `UiCodeBlock` for source code.
|
|
101
|
-
- Put `UiInput` and `UiSelect` in `UiField` with a visible label. Supply help
|
|
102
|
-
text or an error when needed so the field exposes the matching ARIA relationship.
|
|
103
|
-
- Select menus use the same surface, type, radius, and spacing tokens as other
|
|
104
|
-
controls. Keep option rows compact, show a checkmark for the selected value, and
|
|
105
|
-
use quiet hover fills with visible keyboard focus.
|
|
106
|
-
- Use raised surfaces and subtle borders for standalone cards. Align title, content,
|
|
107
|
-
and footer insets; use a quiet divider for footer metadata and avoid card shadows.
|
|
108
|
-
- Keep borders quiet and skip decorative grids. Preserve visible
|
|
109
|
-
`:focus-visible` states, use 44px targets for touch-oriented actions, and provide
|
|
110
|
-
an accessible name for every interactive control.
|
|
111
|
-
|
|
112
|
-
## VS Code themes
|
|
113
|
-
|
|
114
|
-
The browser-safe theme entry point parses VS Code JSON/JSONC and maps supported
|
|
115
|
-
workbench colors into the package's finite semantic token contract:
|
|
56
|
+
Track page sections from a component's setup function:
|
|
116
57
|
|
|
117
58
|
```ts
|
|
118
|
-
import {
|
|
59
|
+
import { useActiveSection } from '@filipgutica/ui'
|
|
119
60
|
|
|
120
|
-
const
|
|
121
|
-
|
|
61
|
+
const activeSection = useActiveSection({
|
|
62
|
+
targetIds: ['overview', 'install', 'commands'],
|
|
63
|
+
})
|
|
122
64
|
```
|
|
123
65
|
|
|
124
|
-
The
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
66
|
+
The returned readonly ref contains the last supplied heading above the root's
|
|
67
|
+
CSS `scroll-padding-top` inset, or the final section at the document bottom.
|
|
68
|
+
IDs must remain stable and in page order; missing elements are ignored after
|
|
69
|
+
mounting. Bind matching links to `aria-current="location"`. Scrolling, resizing,
|
|
70
|
+
layout changes, completed transitions, and restored pages update the value.
|
|
71
|
+
The composable does not change focus, the URL, or browser history, and removes
|
|
72
|
+
its listeners and observer when the component unmounts. During SSR it returns
|
|
73
|
+
the first supplied ID without accessing browser globals.
|
|
132
74
|
|
|
133
|
-
|
|
134
|
-
identifiers and archive paths, caps downloaded and expanded sizes, checks the
|
|
135
|
-
advertised SHA-256 checksum, and retains only normalized colors. Consumers that
|
|
136
|
-
redistribute theme assets remain responsible for their licenses.
|
|
75
|
+
## Themes
|
|
137
76
|
|
|
138
|
-
|
|
77
|
+
Import a VS Code JSON or JSONC theme in the browser:
|
|
139
78
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
this package so applications keep a consistent design language.
|
|
143
|
-
- `@filipgutica/ui/open-vsx` is Node-only. Do not import it into browser bundles.
|
|
144
|
-
|
|
145
|
-
## Docs and sandbox
|
|
146
|
-
|
|
147
|
-
Start the component docs and sandbox:
|
|
148
|
-
|
|
149
|
-
```sh
|
|
150
|
-
pnpm docs:dev
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
Build the provider-neutral static assets:
|
|
154
|
-
|
|
155
|
-
```sh
|
|
156
|
-
pnpm docs:build
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
The docs use hash routes and relative asset paths, so the static assets work
|
|
160
|
-
under a repository path without rewrite rules.
|
|
161
|
-
The local Vite server also provides the same-origin `/api/open-vsx` endpoint.
|
|
162
|
-
The theme picker uses this endpoint to search and import Open VSX themes.
|
|
163
|
-
|
|
164
|
-
### GitHub Pages
|
|
165
|
-
|
|
166
|
-
The GitHub Pages site URL is [filipgutica.github.io/ui](https://filipgutica.github.io/ui/).
|
|
167
|
-
The `docs.yml` workflow builds and deploys `docs-dist` on every push to `main`.
|
|
168
|
-
It can also be started manually from the Actions tab. In the repository's
|
|
169
|
-
**Settings → Pages**, select **GitHub Actions** as the build and deployment source.
|
|
170
|
-
|
|
171
|
-
GitHub Pages serves static files and cannot run the Open VSX API. The workflow
|
|
172
|
-
sets `VITE_STATIC_HOST=true` to disable live Open VSX search and imports;
|
|
173
|
-
built-in themes and local theme-file imports remain available. To build the
|
|
174
|
-
same static configuration locally:
|
|
79
|
+
```ts
|
|
80
|
+
import { applySemanticTheme, parseVsCodeTheme } from '@filipgutica/ui/theme'
|
|
175
81
|
|
|
176
|
-
|
|
177
|
-
|
|
82
|
+
const theme = parseVsCodeTheme({ source, fileName })
|
|
83
|
+
applySemanticTheme({ root: document.documentElement, theme })
|
|
178
84
|
```
|
|
179
85
|
|
|
180
|
-
|
|
181
|
-
|
|
86
|
+
Themes supply colors; the library keeps typography, spacing, and component
|
|
87
|
+
structure consistent. Open VSX search and imports are available through
|
|
88
|
+
`@filipgutica/ui/open-vsx`, which must run on a Node server.
|
|
182
89
|
|
|
183
90
|
## Development
|
|
184
91
|
|
|
185
92
|
```sh
|
|
186
93
|
pnpm install
|
|
94
|
+
pnpm docs:dev
|
|
187
95
|
pnpm verify
|
|
188
96
|
```
|
|
189
|
-
|
|
190
|
-
Commits follow the Conventional Commits format. Releases run from `main`:
|
|
191
|
-
|
|
192
|
-
- `fix:` publishes a patch release
|
|
193
|
-
- `feat:` publishes a minor release
|
|
194
|
-
- `feat!:` publishes a major release
|
|
195
|
-
- `BREAKING CHANGE:` or `[BREAKING CHANGE]` anywhere in a commit body publishes
|
|
196
|
-
a major release
|
|
197
|
-
- the same markers anywhere in an associated merged PR body publish a major
|
|
198
|
-
release
|
|
199
|
-
|
|
200
|
-
The `release.yml` workflow runs on every push to `main`. When semantic-release
|
|
201
|
-
finds qualifying commits, it publishes the next version to npm, creates the
|
|
202
|
-
matching `v<version>` tag, and creates a GitHub Release. Configure an `NPM_TOKEN`
|
|
203
|
-
Actions secret with permission to publish `@filipgutica/ui`; contributors do not
|
|
204
|
-
publish locally or add a token to the repository.
|
|
205
|
-
|
|
206
|
-
Before the first token-backed release, remove the package's existing trusted
|
|
207
|
-
publisher in npm. The workflow retains `id-token: write` only for the provenance
|
|
208
|
-
attestations enabled in `package.json`; npm registry authentication comes from
|
|
209
|
-
the `NPM_TOKEN` secret.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface UiDrawerProps {
|
|
2
|
+
open: boolean;
|
|
3
|
+
title: string;
|
|
4
|
+
}
|
|
5
|
+
type __VLS_Slots = {
|
|
6
|
+
default(): unknown;
|
|
7
|
+
trigger?(): unknown;
|
|
8
|
+
};
|
|
9
|
+
declare const __VLS_base: import("vue").DefineComponent<UiDrawerProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
10
|
+
"update:open": (value: boolean) => any;
|
|
11
|
+
closeAutoFocus: (value: Event) => any;
|
|
12
|
+
}, string, import("vue").PublicProps, Readonly<UiDrawerProps> & Readonly<{
|
|
13
|
+
"onUpdate:open"?: (value: boolean) => any;
|
|
14
|
+
onCloseAutoFocus?: (value: Event) => any;
|
|
15
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
17
|
+
declare const _default: typeof __VLS_export;
|
|
18
|
+
export default _default;
|
|
19
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
20
|
+
new (): {
|
|
21
|
+
$slots: S;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface UiTabItem {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
export interface UiTabsProps {
|
|
6
|
+
items: readonly UiTabItem[];
|
|
7
|
+
label: string;
|
|
8
|
+
modelValue: string;
|
|
9
|
+
}
|
|
10
|
+
type __VLS_Slots = {
|
|
11
|
+
panel(props: {
|
|
12
|
+
value: string;
|
|
13
|
+
}): unknown;
|
|
14
|
+
};
|
|
15
|
+
declare const __VLS_base: import("vue").DefineComponent<UiTabsProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
16
|
+
"update:modelValue": (value: string) => any;
|
|
17
|
+
}, string, import("vue").PublicProps, Readonly<UiTabsProps> & Readonly<{
|
|
18
|
+
"onUpdate:modelValue"?: (value: string) => any;
|
|
19
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
21
|
+
declare const _default: typeof __VLS_export;
|
|
22
|
+
export default _default;
|
|
23
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
24
|
+
new (): {
|
|
25
|
+
$slots: S;
|
|
26
|
+
};
|
|
27
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export { default as UiCheckbox } from './components/UiCheckbox.vue';
|
|
|
12
12
|
export type { UiCheckboxProps } from './components/UiCheckbox.vue';
|
|
13
13
|
export { default as UiDialog } from './components/UiDialog.vue';
|
|
14
14
|
export type { UiDialogProps } from './components/UiDialog.vue';
|
|
15
|
+
export { default as UiDrawer } from './components/UiDrawer.vue';
|
|
16
|
+
export type { UiDrawerProps } from './components/UiDrawer.vue';
|
|
15
17
|
export { default as UiField } from './components/UiField.vue';
|
|
16
18
|
export type { UiFieldProps } from './components/UiField.vue';
|
|
17
19
|
export { default as UiInput } from './components/UiInput.vue';
|
|
@@ -26,7 +28,11 @@ export { default as UiSelect } from './components/UiSelect.vue';
|
|
|
26
28
|
export type { UiSelectProps } from './components/UiSelect.vue';
|
|
27
29
|
export { default as UiSlider } from './components/UiSlider.vue';
|
|
28
30
|
export type { UiSliderProps } from './components/UiSlider.vue';
|
|
31
|
+
export { default as UiTabs } from './components/UiTabs.vue';
|
|
32
|
+
export type { UiTabItem, UiTabsProps } from './components/UiTabs.vue';
|
|
29
33
|
/** @deprecated Use UiCard for structured content. */
|
|
30
34
|
export { default as UiSurface } from './components/UiSurface.vue';
|
|
31
35
|
export type { UiSurfaceProps } from './components/UiSurface.vue';
|
|
32
36
|
export type { UiControlSize } from './control.js';
|
|
37
|
+
export { useActiveSection } from './use-active-section.js';
|
|
38
|
+
export type { ActiveSectionOptions } from './use-active-section.js';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Fragment as e, computed as t, createBlock as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, createTextVNode as o, createVNode as s, defineComponent as c, inject as l, mergeModels as u, mergeProps as d, normalizeStyle as f,
|
|
2
|
-
import { CheckboxIndicator as
|
|
3
|
-
import { useTimeoutFn as
|
|
1
|
+
import { Fragment as e, computed as t, createBlock as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, createTextVNode as o, createVNode as s, defineComponent as c, inject as l, mergeModels as u, mergeProps as d, normalizeStyle as f, onMounted as p, onUnmounted as m, openBlock as h, provide as g, readonly as _, ref as v, renderList as y, renderSlot as b, resolveDynamicComponent as x, shallowRef as ee, toDisplayString as S, unref as C, useId as te, useModel as w, useSlots as T, watch as E, withCtx as D } from "vue";
|
|
2
|
+
import { CheckboxIndicator as O, CheckboxRoot as ne, DialogClose as k, DialogContent as A, DialogDescription as j, DialogOverlay as M, DialogPortal as N, DialogRoot as P, DialogTitle as F, DialogTrigger as I, Primitive as L, ProgressIndicator as R, ProgressRoot as z, RadioGroupItem as B, RadioGroupRoot as V, SliderRange as H, SliderRoot as U, SliderThumb as W, SliderTrack as G } from "reka-ui";
|
|
3
|
+
import { useTimeoutFn as K } from "@vueuse/core";
|
|
4
4
|
//#endregion
|
|
5
5
|
//#region src/components/UiButton.vue
|
|
6
|
-
var
|
|
6
|
+
var q = /* @__PURE__ */ c({
|
|
7
7
|
__name: "UiButton",
|
|
8
8
|
props: {
|
|
9
9
|
disabled: {
|
|
@@ -19,7 +19,7 @@ var U = /* @__PURE__ */ c({
|
|
|
19
19
|
variant: { default: "primary" }
|
|
20
20
|
},
|
|
21
21
|
setup(e) {
|
|
22
|
-
return (t, r) => (
|
|
22
|
+
return (t, r) => (h(), n(C(L), {
|
|
23
23
|
as: "button",
|
|
24
24
|
class: "fg-button",
|
|
25
25
|
"data-size": e.size,
|
|
@@ -28,7 +28,7 @@ var U = /* @__PURE__ */ c({
|
|
|
28
28
|
disabled: e.disabled || e.loading,
|
|
29
29
|
type: e.type
|
|
30
30
|
}, {
|
|
31
|
-
default:
|
|
31
|
+
default: D(() => [b(t.$slots, "default")]),
|
|
32
32
|
_: 3
|
|
33
33
|
}, 8, [
|
|
34
34
|
"data-size",
|
|
@@ -38,49 +38,49 @@ var U = /* @__PURE__ */ c({
|
|
|
38
38
|
"type"
|
|
39
39
|
]));
|
|
40
40
|
}
|
|
41
|
-
}),
|
|
41
|
+
}), J = { class: "fg-card" }, Y = {
|
|
42
42
|
key: 0,
|
|
43
43
|
class: "fg-card__header"
|
|
44
|
-
},
|
|
44
|
+
}, X = {
|
|
45
45
|
key: 1,
|
|
46
46
|
class: "fg-card__actions"
|
|
47
|
-
},
|
|
47
|
+
}, Z = {
|
|
48
48
|
key: 1,
|
|
49
49
|
class: "fg-card__body"
|
|
50
|
-
},
|
|
50
|
+
}, re = {
|
|
51
51
|
key: 2,
|
|
52
52
|
class: "fg-card__footer"
|
|
53
|
-
},
|
|
53
|
+
}, ie = /* @__PURE__ */ c({
|
|
54
54
|
__name: "UiCard",
|
|
55
55
|
props: {
|
|
56
56
|
title: { default: () => void 0 },
|
|
57
57
|
titleTag: { default: "h3" }
|
|
58
58
|
},
|
|
59
59
|
setup(e) {
|
|
60
|
-
let t =
|
|
61
|
-
return (a, s) => (
|
|
62
|
-
e.title || t.title || t.actions ? (
|
|
60
|
+
let t = T();
|
|
61
|
+
return (a, s) => (h(), i("div", J, [
|
|
62
|
+
e.title || t.title || t.actions ? (h(), i("header", Y, [e.title || t.title ? (h(), n(x(e.titleTag), {
|
|
63
63
|
key: 0,
|
|
64
64
|
class: "fg-card__title"
|
|
65
65
|
}, {
|
|
66
|
-
default:
|
|
66
|
+
default: D(() => [b(a.$slots, "title", {}, () => [o(S(e.title), 1)])]),
|
|
67
67
|
_: 3
|
|
68
|
-
})) : r("", !0), t.actions ? (
|
|
69
|
-
t.default ? (
|
|
70
|
-
t.footer ? (
|
|
68
|
+
})) : r("", !0), t.actions ? (h(), i("div", X, [b(a.$slots, "actions")])) : r("", !0)])) : r("", !0),
|
|
69
|
+
t.default ? (h(), i("div", Z, [b(a.$slots, "default")])) : r("", !0),
|
|
70
|
+
t.footer ? (h(), i("footer", re, [b(a.$slots, "footer")])) : r("", !0)
|
|
71
71
|
]));
|
|
72
72
|
}
|
|
73
|
-
}),
|
|
73
|
+
}), ae = ["data-wrap", "data-line-numbers"], oe = { class: "fg-code-block__header" }, se = { class: "fg-code-block__title" }, ce = {
|
|
74
74
|
key: 0,
|
|
75
75
|
class: "fg-code-block__language"
|
|
76
|
-
},
|
|
76
|
+
}, le = { class: "fg-code-block__actions" }, ue = ["disabled"], de = ["aria-label"], fe = {
|
|
77
77
|
key: 0,
|
|
78
78
|
class: "fg-code-block__number",
|
|
79
79
|
"aria-hidden": "true"
|
|
80
|
-
},
|
|
80
|
+
}, pe = { class: "fg-code-block__text" }, me = {
|
|
81
81
|
class: "fg-code-block__status",
|
|
82
82
|
role: "status"
|
|
83
|
-
},
|
|
83
|
+
}, he = /* @__PURE__ */ c({
|
|
84
84
|
__name: "UiCodeBlock",
|
|
85
85
|
props: {
|
|
86
86
|
code: {},
|
|
@@ -100,7 +100,7 @@ var U = /* @__PURE__ */ c({
|
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
102
|
setup(n) {
|
|
103
|
-
let s = ee(), c =
|
|
103
|
+
let s = ee(), c = v(""), l = t(() => s.value ?? n.code.split("\n").map((e) => [{ content: e }])), u = {
|
|
104
104
|
vue: "vue",
|
|
105
105
|
ts: "typescript",
|
|
106
106
|
typescript: "typescript",
|
|
@@ -113,7 +113,7 @@ var U = /* @__PURE__ */ c({
|
|
|
113
113
|
sh: "bash",
|
|
114
114
|
shell: "bash"
|
|
115
115
|
};
|
|
116
|
-
|
|
116
|
+
E(() => [n.code, n.language], async ([e, t], n, r) => {
|
|
117
117
|
let i = !1;
|
|
118
118
|
r(() => {
|
|
119
119
|
i = !0;
|
|
@@ -136,37 +136,37 @@ var U = /* @__PURE__ */ c({
|
|
|
136
136
|
i || (c.value = "Syntax highlighting unavailable. Showing plain text.");
|
|
137
137
|
}
|
|
138
138
|
}, { immediate: !0 });
|
|
139
|
-
let d =
|
|
139
|
+
let d = v(!1), { start: p } = K(() => {
|
|
140
140
|
d.value = !1;
|
|
141
|
-
}, 1500, { immediate: !1 }),
|
|
142
|
-
if (
|
|
143
|
-
|
|
141
|
+
}, 1500, { immediate: !1 }), m = v(!1), g = v(""), _ = async () => {
|
|
142
|
+
if (g.value = "", d.value = !1, typeof navigator > "u" || !navigator.clipboard?.writeText) {
|
|
143
|
+
g.value = "Clipboard unavailable. Select the code to copy it.";
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
146
|
-
|
|
146
|
+
m.value = !0;
|
|
147
147
|
try {
|
|
148
|
-
await navigator.clipboard.writeText(n.code), d.value = !0,
|
|
148
|
+
await navigator.clipboard.writeText(n.code), d.value = !0, p();
|
|
149
149
|
} catch {
|
|
150
|
-
|
|
150
|
+
g.value = "Could not copy. Select the code to copy it.";
|
|
151
151
|
} finally {
|
|
152
|
-
|
|
152
|
+
m.value = !1;
|
|
153
153
|
}
|
|
154
154
|
};
|
|
155
|
-
return (t, s) => (
|
|
155
|
+
return (t, s) => (h(), i("div", {
|
|
156
156
|
class: "fg-code-block",
|
|
157
157
|
"data-wrap": n.wrap,
|
|
158
158
|
"data-line-numbers": n.lineNumbers
|
|
159
159
|
}, [
|
|
160
|
-
a("div",
|
|
161
|
-
a("span",
|
|
162
|
-
n.title ? (
|
|
163
|
-
a("div",
|
|
160
|
+
a("div", oe, [
|
|
161
|
+
a("span", se, S(n.title || n.language), 1),
|
|
162
|
+
n.title ? (h(), i("span", ce, S(n.language), 1)) : r("", !0),
|
|
163
|
+
a("div", le, [b(t.$slots, "actions"), n.copyable ? (h(), i("button", {
|
|
164
164
|
key: 0,
|
|
165
165
|
type: "button",
|
|
166
166
|
class: "fg-code-block__copy",
|
|
167
|
-
disabled:
|
|
167
|
+
disabled: m.value,
|
|
168
168
|
"aria-label": "Copy code",
|
|
169
|
-
onClick:
|
|
169
|
+
onClick: _
|
|
170
170
|
}, [s[0] ||= a("svg", {
|
|
171
171
|
viewBox: "0 0 24 24",
|
|
172
172
|
fill: "none",
|
|
@@ -179,50 +179,50 @@ var U = /* @__PURE__ */ c({
|
|
|
179
179
|
width: "12",
|
|
180
180
|
height: "13",
|
|
181
181
|
rx: "2"
|
|
182
|
-
}), a("path", { d: "M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3" })], -1), o(" " +
|
|
182
|
+
}), a("path", { d: "M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3" })], -1), o(" " + S(d.value && !g.value ? "Copied" : "Copy"), 1)], 8, ue)) : r("", !0)])
|
|
183
183
|
]),
|
|
184
184
|
a("pre", {
|
|
185
185
|
class: "fg-code-block__pre",
|
|
186
186
|
tabindex: "0",
|
|
187
187
|
"aria-label": n.title || `${n.language} code`
|
|
188
|
-
}, [a("code", null, [(
|
|
188
|
+
}, [a("code", null, [(h(!0), i(e, null, y(l.value, (t, s) => (h(), i("span", {
|
|
189
189
|
key: s,
|
|
190
190
|
class: "fg-code-block__line"
|
|
191
|
-
}, [n.lineNumbers ? (
|
|
191
|
+
}, [n.lineNumbers ? (h(), i("span", fe, S(s + 1), 1)) : r("", !0), a("span", pe, [(h(!0), i(e, null, y(t, (e, t) => (h(), i("span", {
|
|
192
192
|
key: t,
|
|
193
193
|
style: f({ color: e.color })
|
|
194
|
-
},
|
|
195
|
-
a("span",
|
|
196
|
-
], 8,
|
|
194
|
+
}, S(e.content), 5))), 128)), o(S(s < l.value.length - 1 ? "\n" : ""), 1)])]))), 128))])], 8, de),
|
|
195
|
+
a("span", me, S(g.value || (d.value ? "Code copied to clipboard." : c.value)), 1)
|
|
196
|
+
], 8, ae));
|
|
197
197
|
}
|
|
198
|
-
}),
|
|
198
|
+
}), ge = /* @__PURE__ */ c({
|
|
199
199
|
__name: "UiAlert",
|
|
200
200
|
props: { tone: { default: "neutral" } },
|
|
201
201
|
setup(e) {
|
|
202
|
-
return (t, r) => (
|
|
202
|
+
return (t, r) => (h(), n(C(L), {
|
|
203
203
|
as: "div",
|
|
204
204
|
class: "fg-alert",
|
|
205
205
|
"data-tone": e.tone,
|
|
206
206
|
role: e.tone === "danger" || e.tone === "error" ? "alert" : "status"
|
|
207
207
|
}, {
|
|
208
|
-
default:
|
|
208
|
+
default: D(() => [b(t.$slots, "default")]),
|
|
209
209
|
_: 3
|
|
210
210
|
}, 8, ["data-tone", "role"]));
|
|
211
211
|
}
|
|
212
|
-
}),
|
|
212
|
+
}), _e = /* @__PURE__ */ c({
|
|
213
213
|
__name: "UiBadge",
|
|
214
214
|
props: { tone: { default: "neutral" } },
|
|
215
215
|
setup(e) {
|
|
216
|
-
return (t, r) => (
|
|
216
|
+
return (t, r) => (h(), n(C(L), {
|
|
217
217
|
as: "span",
|
|
218
218
|
class: "fg-badge",
|
|
219
219
|
"data-tone": e.tone
|
|
220
220
|
}, {
|
|
221
|
-
default:
|
|
221
|
+
default: D(() => [b(t.$slots, "default")]),
|
|
222
222
|
_: 3
|
|
223
223
|
}, 8, ["data-tone"]));
|
|
224
224
|
}
|
|
225
|
-
}),
|
|
225
|
+
}), ve = ["data-size", "data-disabled"], ye = { class: "fg-checkbox__label" }, be = /* @__PURE__ */ c({
|
|
226
226
|
__name: "UiCheckbox",
|
|
227
227
|
props: {
|
|
228
228
|
disabled: {
|
|
@@ -240,27 +240,28 @@ var U = /* @__PURE__ */ c({
|
|
|
240
240
|
let n = t, r = (e) => {
|
|
241
241
|
n("update:modelValue", e === !0);
|
|
242
242
|
};
|
|
243
|
-
return (t, n) => (
|
|
243
|
+
return (t, n) => (h(), i("label", {
|
|
244
244
|
class: "fg-checkbox",
|
|
245
245
|
"data-size": e.size,
|
|
246
246
|
"data-disabled": e.disabled ? "true" : void 0
|
|
247
|
-
}, [s(
|
|
247
|
+
}, [s(C(ne), {
|
|
248
248
|
class: "fg-checkbox__control",
|
|
249
249
|
disabled: e.disabled,
|
|
250
250
|
"model-value": e.modelValue,
|
|
251
251
|
"onUpdate:modelValue": r
|
|
252
252
|
}, {
|
|
253
|
-
default:
|
|
254
|
-
default:
|
|
253
|
+
default: D(() => [s(C(O), { class: "fg-checkbox__indicator" }, {
|
|
254
|
+
default: D(() => [...n[0] ||= [o("✓", -1)]]),
|
|
255
255
|
_: 1
|
|
256
256
|
})]),
|
|
257
257
|
_: 1
|
|
258
|
-
}, 8, ["disabled", "model-value"]), a("span",
|
|
258
|
+
}, 8, ["disabled", "model-value"]), a("span", ye, [b(t.$slots, "default")])], 8, ve));
|
|
259
259
|
}
|
|
260
|
-
}),
|
|
260
|
+
}), xe = { class: "fg-dialog__header" }, Se = { class: "fg-dialog__body" }, Ce = {
|
|
261
261
|
key: 0,
|
|
262
262
|
class: "fg-dialog__footer"
|
|
263
|
-
},
|
|
263
|
+
}, we = /* @__PURE__ */ c({
|
|
264
|
+
inheritAttrs: !1,
|
|
264
265
|
__name: "UiDialog",
|
|
265
266
|
props: {
|
|
266
267
|
description: { default: () => void 0 },
|
|
@@ -270,43 +271,86 @@ var U = /* @__PURE__ */ c({
|
|
|
270
271
|
emits: ["update:open"],
|
|
271
272
|
setup(e, { emit: t }) {
|
|
272
273
|
let c = t;
|
|
273
|
-
return (t, l) => (
|
|
274
|
+
return (t, l) => (h(), n(C(P), {
|
|
274
275
|
open: e.open,
|
|
275
276
|
"onUpdate:open": l[0] ||= (e) => c("update:open", e)
|
|
276
277
|
}, {
|
|
277
|
-
default:
|
|
278
|
-
default:
|
|
278
|
+
default: D(() => [s(C(N), null, {
|
|
279
|
+
default: D(() => [s(C(M), { class: "fg-dialog__overlay" }), s(C(A), d({
|
|
279
280
|
class: "fg-dialog__content",
|
|
280
281
|
"aria-modal": "true"
|
|
281
|
-
}, {
|
|
282
|
-
default:
|
|
283
|
-
a("div",
|
|
284
|
-
default:
|
|
282
|
+
}, t.$attrs), {
|
|
283
|
+
default: D(() => [
|
|
284
|
+
a("div", xe, [a("div", null, [s(C(F), { class: "fg-dialog__title" }, {
|
|
285
|
+
default: D(() => [o(S(e.title), 1)]),
|
|
285
286
|
_: 1
|
|
286
|
-
}), e.description ? (
|
|
287
|
+
}), e.description ? (h(), n(C(j), {
|
|
287
288
|
key: 0,
|
|
288
289
|
class: "fg-dialog__description"
|
|
289
290
|
}, {
|
|
290
|
-
default:
|
|
291
|
+
default: D(() => [o(S(e.description), 1)]),
|
|
291
292
|
_: 1
|
|
292
|
-
})) : r("", !0)]), s(
|
|
293
|
+
})) : r("", !0)]), s(C(k), {
|
|
293
294
|
class: "fg-dialog__close",
|
|
294
295
|
"aria-label": "Close dialog"
|
|
295
296
|
}, {
|
|
296
|
-
default:
|
|
297
|
+
default: D(() => [...l[1] ||= [o(" × ", -1)]]),
|
|
297
298
|
_: 1
|
|
298
299
|
})]),
|
|
299
|
-
a("div",
|
|
300
|
-
t.$slots.footer ? (
|
|
300
|
+
a("div", Se, [b(t.$slots, "default")]),
|
|
301
|
+
t.$slots.footer ? (h(), i("div", Ce, [b(t.$slots, "footer")])) : r("", !0)
|
|
301
302
|
]),
|
|
302
303
|
_: 3
|
|
303
|
-
})]),
|
|
304
|
+
}, 16)]),
|
|
304
305
|
_: 3
|
|
305
306
|
})]),
|
|
306
307
|
_: 3
|
|
307
308
|
}, 8, ["open"]));
|
|
308
309
|
}
|
|
309
|
-
}),
|
|
310
|
+
}), Te = { class: "fg-drawer__header" }, Ee = { class: "fg-drawer__body" }, De = /* @__PURE__ */ c({
|
|
311
|
+
inheritAttrs: !1,
|
|
312
|
+
__name: "UiDrawer",
|
|
313
|
+
props: {
|
|
314
|
+
open: { type: Boolean },
|
|
315
|
+
title: {}
|
|
316
|
+
},
|
|
317
|
+
emits: ["update:open", "closeAutoFocus"],
|
|
318
|
+
setup(e, { emit: t }) {
|
|
319
|
+
let i = t;
|
|
320
|
+
return (t, c) => (h(), n(C(P), {
|
|
321
|
+
open: e.open,
|
|
322
|
+
"onUpdate:open": c[1] ||= (e) => i("update:open", e)
|
|
323
|
+
}, {
|
|
324
|
+
default: D(() => [t.$slots.trigger ? (h(), n(C(I), {
|
|
325
|
+
key: 0,
|
|
326
|
+
"as-child": ""
|
|
327
|
+
}, {
|
|
328
|
+
default: D(() => [b(t.$slots, "trigger")]),
|
|
329
|
+
_: 3
|
|
330
|
+
})) : r("", !0), s(C(N), null, {
|
|
331
|
+
default: D(() => [s(C(M), { class: "fg-drawer__overlay" }), s(C(A), d({
|
|
332
|
+
class: "fg-drawer__content",
|
|
333
|
+
"aria-modal": "true",
|
|
334
|
+
"aria-describedby": void 0
|
|
335
|
+
}, t.$attrs, { onCloseAutoFocus: c[0] ||= (e) => i("closeAutoFocus", e) }), {
|
|
336
|
+
default: D(() => [a("div", Te, [s(C(F), { class: "fg-drawer__title" }, {
|
|
337
|
+
default: D(() => [o(S(e.title), 1)]),
|
|
338
|
+
_: 1
|
|
339
|
+
}), s(C(k), {
|
|
340
|
+
class: "fg-drawer__close",
|
|
341
|
+
"aria-label": "Close drawer"
|
|
342
|
+
}, {
|
|
343
|
+
default: D(() => [...c[2] ||= [o(" × ", -1)]]),
|
|
344
|
+
_: 1
|
|
345
|
+
})]), a("div", Ee, [b(t.$slots, "default")])]),
|
|
346
|
+
_: 3
|
|
347
|
+
}, 16)]),
|
|
348
|
+
_: 3
|
|
349
|
+
})]),
|
|
350
|
+
_: 3
|
|
351
|
+
}, 8, ["open"]));
|
|
352
|
+
}
|
|
353
|
+
}), Q = Symbol("UiFieldContext"), Oe = ["data-invalid"], ke = ["for"], Ae = ["id"], je = ["id"], Me = /* @__PURE__ */ c({
|
|
310
354
|
__name: "UiField",
|
|
311
355
|
props: {
|
|
312
356
|
controlId: {},
|
|
@@ -316,38 +360,38 @@ var U = /* @__PURE__ */ c({
|
|
|
316
360
|
},
|
|
317
361
|
setup(e) {
|
|
318
362
|
let n = t(() => e.description && !e.error ? `${e.controlId}-description` : void 0), o = t(() => e.error ? `${e.controlId}-error` : void 0), s = t(() => !!e.error);
|
|
319
|
-
return
|
|
363
|
+
return g(Q, {
|
|
320
364
|
controlId: t(() => e.controlId),
|
|
321
365
|
descriptionId: n,
|
|
322
366
|
errorId: o,
|
|
323
367
|
invalid: s
|
|
324
|
-
}), (t, s) => (
|
|
368
|
+
}), (t, s) => (h(), i("div", {
|
|
325
369
|
class: "fg-field",
|
|
326
370
|
"data-invalid": e.error ? "true" : void 0
|
|
327
371
|
}, [
|
|
328
372
|
a("label", {
|
|
329
373
|
class: "fg-field__label",
|
|
330
374
|
for: e.controlId
|
|
331
|
-
},
|
|
332
|
-
|
|
375
|
+
}, S(e.label), 9, ke),
|
|
376
|
+
b(t.$slots, "default", {
|
|
333
377
|
descriptionId: n.value,
|
|
334
378
|
errorId: o.value
|
|
335
379
|
}),
|
|
336
|
-
e.description && !e.error ? (
|
|
380
|
+
e.description && !e.error ? (h(), i("p", {
|
|
337
381
|
key: 0,
|
|
338
382
|
id: n.value,
|
|
339
383
|
class: "fg-field__description",
|
|
340
384
|
"data-field-description": ""
|
|
341
|
-
},
|
|
342
|
-
e.error ? (
|
|
385
|
+
}, S(e.description), 9, Ae)) : r("", !0),
|
|
386
|
+
e.error ? (h(), i("p", {
|
|
343
387
|
key: 1,
|
|
344
388
|
id: o.value,
|
|
345
389
|
class: "fg-field__error",
|
|
346
390
|
"data-field-error": ""
|
|
347
|
-
},
|
|
348
|
-
], 8,
|
|
391
|
+
}, S(e.error), 9, je)) : r("", !0)
|
|
392
|
+
], 8, Oe));
|
|
349
393
|
}
|
|
350
|
-
}),
|
|
394
|
+
}), Ne = /* @__PURE__ */ c({
|
|
351
395
|
__name: "UiInput",
|
|
352
396
|
props: {
|
|
353
397
|
disabled: {
|
|
@@ -368,13 +412,13 @@ var U = /* @__PURE__ */ c({
|
|
|
368
412
|
let r = l(Q, void 0), i = t, a = (e) => {
|
|
369
413
|
e.currentTarget instanceof HTMLInputElement && i("update:modelValue", e.currentTarget.value);
|
|
370
414
|
};
|
|
371
|
-
return (t, i) => (
|
|
372
|
-
id:
|
|
415
|
+
return (t, i) => (h(), n(C(L), {
|
|
416
|
+
id: C(r)?.controlId.value ?? e.id,
|
|
373
417
|
as: "input",
|
|
374
418
|
class: "fg-input",
|
|
375
|
-
"aria-describedby":
|
|
376
|
-
"aria-errormessage":
|
|
377
|
-
"aria-invalid": e.invalid ||
|
|
419
|
+
"aria-describedby": C(r)?.descriptionId.value,
|
|
420
|
+
"aria-errormessage": C(r)?.errorId.value,
|
|
421
|
+
"aria-invalid": e.invalid || C(r)?.invalid.value ? "true" : void 0,
|
|
378
422
|
"data-size": e.size,
|
|
379
423
|
disabled: e.disabled,
|
|
380
424
|
type: e.type,
|
|
@@ -391,7 +435,7 @@ var U = /* @__PURE__ */ c({
|
|
|
391
435
|
"value"
|
|
392
436
|
]));
|
|
393
437
|
}
|
|
394
|
-
}),
|
|
438
|
+
}), Pe = 100, $ = /* @__PURE__ */ c({
|
|
395
439
|
__name: "UiProgress",
|
|
396
440
|
props: {
|
|
397
441
|
label: {},
|
|
@@ -400,19 +444,19 @@ var U = /* @__PURE__ */ c({
|
|
|
400
444
|
},
|
|
401
445
|
setup(e) {
|
|
402
446
|
let r = t(() => {
|
|
403
|
-
let t = Number.isFinite(e.max) && e.max > 0 ? e.max :
|
|
447
|
+
let t = Number.isFinite(e.max) && e.max > 0 ? e.max : Pe;
|
|
404
448
|
return {
|
|
405
449
|
max: t,
|
|
406
450
|
value: e.value === null || !Number.isFinite(e.value) ? null : Math.min(t, Math.max(0, e.value))
|
|
407
451
|
};
|
|
408
452
|
}), i = t(() => r.value.value === null ? 0 : r.value.value / r.value.max * 100);
|
|
409
|
-
return (t, a) => (
|
|
453
|
+
return (t, a) => (h(), n(C(z), {
|
|
410
454
|
class: "fg-progress",
|
|
411
455
|
"aria-label": e.label,
|
|
412
456
|
max: r.value.max,
|
|
413
457
|
"model-value": r.value.value
|
|
414
458
|
}, {
|
|
415
|
-
default:
|
|
459
|
+
default: D(() => [s(C(R), {
|
|
416
460
|
class: "fg-progress__indicator",
|
|
417
461
|
style: f({ transform: `translateX(-${100 - i.value}%)` })
|
|
418
462
|
}, null, 8, ["style"])]),
|
|
@@ -423,7 +467,7 @@ var U = /* @__PURE__ */ c({
|
|
|
423
467
|
"model-value"
|
|
424
468
|
]));
|
|
425
469
|
}
|
|
426
|
-
}),
|
|
470
|
+
}), Fe = { class: "fg-radio-card__content" }, Ie = /* @__PURE__ */ c({
|
|
427
471
|
__name: "UiRadioCard",
|
|
428
472
|
props: {
|
|
429
473
|
disabled: {
|
|
@@ -433,19 +477,19 @@ var U = /* @__PURE__ */ c({
|
|
|
433
477
|
value: {}
|
|
434
478
|
},
|
|
435
479
|
setup(e) {
|
|
436
|
-
return (t, r) => (
|
|
480
|
+
return (t, r) => (h(), n(C(B), {
|
|
437
481
|
class: "fg-radio-card",
|
|
438
482
|
disabled: e.disabled,
|
|
439
483
|
value: e.value
|
|
440
484
|
}, {
|
|
441
|
-
default:
|
|
485
|
+
default: D(() => [r[0] ||= a("span", {
|
|
442
486
|
class: "fg-radio-card__indicator",
|
|
443
487
|
"aria-hidden": "true"
|
|
444
|
-
}, null, -1), a("span",
|
|
488
|
+
}, null, -1), a("span", Fe, [b(t.$slots, "default")])]),
|
|
445
489
|
_: 3
|
|
446
490
|
}, 8, ["disabled", "value"]));
|
|
447
491
|
}
|
|
448
|
-
}),
|
|
492
|
+
}), Le = /* @__PURE__ */ c({
|
|
449
493
|
__name: "UiRadioCardGroup",
|
|
450
494
|
props: /*@__PURE__*/ u({
|
|
451
495
|
disabled: {
|
|
@@ -463,7 +507,7 @@ var U = /* @__PURE__ */ c({
|
|
|
463
507
|
}),
|
|
464
508
|
emits: ["update:modelValue"],
|
|
465
509
|
setup(e) {
|
|
466
|
-
let t =
|
|
510
|
+
let t = w(e, "modelValue"), r = (t) => {
|
|
467
511
|
if (t.altKey || t.ctrlKey || t.metaKey || t.shiftKey) return;
|
|
468
512
|
let n = t.currentTarget, r = t.target;
|
|
469
513
|
if (!(n instanceof HTMLElement) || !(r instanceof HTMLElement)) return;
|
|
@@ -479,14 +523,14 @@ var U = /* @__PURE__ */ c({
|
|
|
479
523
|
let d = o[u];
|
|
480
524
|
!d || d === r || (t.preventDefault(), d.focus(), d.click());
|
|
481
525
|
};
|
|
482
|
-
return (i, a) => (
|
|
526
|
+
return (i, a) => (h(), n(C(V), d({
|
|
483
527
|
modelValue: t.value,
|
|
484
528
|
"onUpdate:modelValue": a[0] ||= (e) => t.value = e,
|
|
485
529
|
class: "fg-radio-card-group",
|
|
486
530
|
disabled: e.disabled,
|
|
487
531
|
loop: e.loop
|
|
488
532
|
}, e.orientation === void 0 ? {} : { orientation: e.orientation }, { onKeydown: r }), {
|
|
489
|
-
default:
|
|
533
|
+
default: D(() => [b(i.$slots, "default")]),
|
|
490
534
|
_: 3
|
|
491
535
|
}, 16, [
|
|
492
536
|
"modelValue",
|
|
@@ -494,7 +538,7 @@ var U = /* @__PURE__ */ c({
|
|
|
494
538
|
"loop"
|
|
495
539
|
]));
|
|
496
540
|
}
|
|
497
|
-
}),
|
|
541
|
+
}), Re = /* @__PURE__ */ c({
|
|
498
542
|
__name: "UiSelect",
|
|
499
543
|
props: {
|
|
500
544
|
disabled: {
|
|
@@ -514,19 +558,19 @@ var U = /* @__PURE__ */ c({
|
|
|
514
558
|
let r = l(Q, void 0), i = t, a = (e) => {
|
|
515
559
|
e.currentTarget instanceof HTMLSelectElement && i("update:modelValue", e.currentTarget.value);
|
|
516
560
|
};
|
|
517
|
-
return (t, i) => (
|
|
518
|
-
id:
|
|
561
|
+
return (t, i) => (h(), n(C(L), {
|
|
562
|
+
id: C(r)?.controlId.value ?? e.id,
|
|
519
563
|
as: "select",
|
|
520
564
|
class: "fg-select",
|
|
521
|
-
"aria-describedby":
|
|
522
|
-
"aria-errormessage":
|
|
523
|
-
"aria-invalid": e.invalid ||
|
|
565
|
+
"aria-describedby": C(r)?.descriptionId.value,
|
|
566
|
+
"aria-errormessage": C(r)?.errorId.value,
|
|
567
|
+
"aria-invalid": e.invalid || C(r)?.invalid.value ? "true" : void 0,
|
|
524
568
|
"data-size": e.size,
|
|
525
569
|
disabled: e.disabled,
|
|
526
570
|
value: e.modelValue,
|
|
527
571
|
onChange: a
|
|
528
572
|
}, {
|
|
529
|
-
default:
|
|
573
|
+
default: D(() => [b(t.$slots, "default")]),
|
|
530
574
|
_: 3
|
|
531
575
|
}, 8, [
|
|
532
576
|
"id",
|
|
@@ -538,7 +582,7 @@ var U = /* @__PURE__ */ c({
|
|
|
538
582
|
"value"
|
|
539
583
|
]));
|
|
540
584
|
}
|
|
541
|
-
}),
|
|
585
|
+
}), ze = /* @__PURE__ */ c({
|
|
542
586
|
__name: "UiSlider",
|
|
543
587
|
props: {
|
|
544
588
|
disabled: {
|
|
@@ -571,7 +615,7 @@ var U = /* @__PURE__ */ c({
|
|
|
571
615
|
e !== void 0 && o("update:modelValue", e);
|
|
572
616
|
}
|
|
573
617
|
});
|
|
574
|
-
return (t, r) => (
|
|
618
|
+
return (t, r) => (h(), n(C(U), {
|
|
575
619
|
modelValue: c.value,
|
|
576
620
|
"onUpdate:modelValue": r[0] ||= (e) => c.value = e,
|
|
577
621
|
class: "fg-slider",
|
|
@@ -581,15 +625,15 @@ var U = /* @__PURE__ */ c({
|
|
|
581
625
|
min: a.value.min,
|
|
582
626
|
step: a.value.step
|
|
583
627
|
}, {
|
|
584
|
-
default:
|
|
585
|
-
default:
|
|
628
|
+
default: D(() => [s(C(G), { class: "fg-slider__track" }, {
|
|
629
|
+
default: D(() => [s(C(H), { class: "fg-slider__range" })]),
|
|
586
630
|
_: 1
|
|
587
|
-
}), s(
|
|
588
|
-
id:
|
|
631
|
+
}), s(C(W), {
|
|
632
|
+
id: C(i)?.controlId.value ?? e.id,
|
|
589
633
|
class: "fg-slider__thumb",
|
|
590
|
-
"aria-describedby":
|
|
591
|
-
"aria-errormessage":
|
|
592
|
-
"aria-invalid":
|
|
634
|
+
"aria-describedby": C(i)?.descriptionId.value,
|
|
635
|
+
"aria-errormessage": C(i)?.errorId.value,
|
|
636
|
+
"aria-invalid": C(i)?.invalid.value ? "true" : void 0,
|
|
593
637
|
"aria-label": e.label
|
|
594
638
|
}, null, 8, [
|
|
595
639
|
"id",
|
|
@@ -608,22 +652,118 @@ var U = /* @__PURE__ */ c({
|
|
|
608
652
|
"step"
|
|
609
653
|
]));
|
|
610
654
|
}
|
|
611
|
-
}),
|
|
655
|
+
}), Be = { class: "fg-tabs" }, Ve = ["aria-label"], He = [
|
|
656
|
+
"id",
|
|
657
|
+
"aria-controls",
|
|
658
|
+
"aria-selected",
|
|
659
|
+
"tabindex",
|
|
660
|
+
"onClick",
|
|
661
|
+
"onFocus"
|
|
662
|
+
], Ue = [
|
|
663
|
+
"id",
|
|
664
|
+
"role",
|
|
665
|
+
"aria-labelledby",
|
|
666
|
+
"hidden",
|
|
667
|
+
"tabindex"
|
|
668
|
+
], We = ["id", "hidden"], Ge = /* @__PURE__ */ c({
|
|
669
|
+
__name: "UiTabs",
|
|
670
|
+
props: {
|
|
671
|
+
items: {},
|
|
672
|
+
label: {},
|
|
673
|
+
modelValue: {}
|
|
674
|
+
},
|
|
675
|
+
emits: ["update:modelValue"],
|
|
676
|
+
setup(n, { emit: o }) {
|
|
677
|
+
let s = o, c = te(), l = v(!1), u = t(() => n.items.some((e) => e.value === n.modelValue) ? n.modelValue : n.items[0]?.value), d = (e) => `${c}-tab-${encodeURIComponent(e)}`, f = (e) => `${c}-panel-${encodeURIComponent(e)}`, m = (e) => `${c}-heading-${encodeURIComponent(e)}`;
|
|
678
|
+
p(() => {
|
|
679
|
+
l.value = !0;
|
|
680
|
+
});
|
|
681
|
+
let g = (e) => {
|
|
682
|
+
e !== u.value && s("update:modelValue", e);
|
|
683
|
+
}, _ = (e) => {
|
|
684
|
+
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || ![
|
|
685
|
+
"ArrowLeft",
|
|
686
|
+
"ArrowRight",
|
|
687
|
+
"Home",
|
|
688
|
+
"End"
|
|
689
|
+
].includes(e.key)) return;
|
|
690
|
+
let t = e.currentTarget, n = e.target;
|
|
691
|
+
if (!(t instanceof HTMLElement) || !(n instanceof HTMLButtonElement)) return;
|
|
692
|
+
let r = [...t.querySelectorAll("[role=\"tab\"]")], i = r.indexOf(n);
|
|
693
|
+
if (i < 0 || r.length === 0) return;
|
|
694
|
+
let a = getComputedStyle(t).direction === "rtl", o = e.key === (a ? "ArrowRight" : "ArrowLeft"), s = r[e.key === "Home" ? 0 : e.key === "End" ? r.length - 1 : (i + (o ? -1 : 1) + r.length) % r.length];
|
|
695
|
+
s && (e.preventDefault(), s.focus());
|
|
696
|
+
};
|
|
697
|
+
return (t, o) => (h(), i("div", Be, [l.value ? (h(), i("div", {
|
|
698
|
+
key: 0,
|
|
699
|
+
class: "fg-tabs__list",
|
|
700
|
+
role: "tablist",
|
|
701
|
+
"aria-label": n.label,
|
|
702
|
+
onKeydown: _
|
|
703
|
+
}, [(h(!0), i(e, null, y(n.items, (e) => (h(), i("button", {
|
|
704
|
+
id: d(e.value),
|
|
705
|
+
key: e.value,
|
|
706
|
+
type: "button",
|
|
707
|
+
class: "fg-tabs__tab",
|
|
708
|
+
role: "tab",
|
|
709
|
+
"aria-controls": f(e.value),
|
|
710
|
+
"aria-selected": e.value === u.value,
|
|
711
|
+
tabindex: e.value === u.value ? 0 : -1,
|
|
712
|
+
onClick: (t) => g(e.value),
|
|
713
|
+
onFocus: (t) => g(e.value)
|
|
714
|
+
}, S(e.label), 41, He))), 128))], 40, Ve)) : r("", !0), (h(!0), i(e, null, y(n.items, (e) => (h(), i("section", {
|
|
715
|
+
id: f(e.value),
|
|
716
|
+
key: e.value,
|
|
717
|
+
class: "fg-tabs__panel",
|
|
718
|
+
role: l.value ? "tabpanel" : void 0,
|
|
719
|
+
"aria-labelledby": l.value ? d(e.value) : m(e.value),
|
|
720
|
+
hidden: l.value && e.value !== u.value,
|
|
721
|
+
tabindex: l.value ? 0 : void 0
|
|
722
|
+
}, [a("h3", {
|
|
723
|
+
id: m(e.value),
|
|
724
|
+
class: "fg-tabs__heading",
|
|
725
|
+
hidden: l.value
|
|
726
|
+
}, S(e.label), 9, We), b(t.$slots, "panel", { value: e.value })], 8, Ue))), 128))]));
|
|
727
|
+
}
|
|
728
|
+
}), Ke = /* @__PURE__ */ c({
|
|
612
729
|
__name: "UiSurface",
|
|
613
730
|
props: {
|
|
614
731
|
as: { default: "section" },
|
|
615
732
|
padding: { default: "default" }
|
|
616
733
|
},
|
|
617
734
|
setup(e) {
|
|
618
|
-
return (t, r) => (
|
|
735
|
+
return (t, r) => (h(), n(C(L), {
|
|
619
736
|
as: e.as,
|
|
620
737
|
class: "fg-surface",
|
|
621
738
|
"data-padding": e.padding
|
|
622
739
|
}, {
|
|
623
|
-
default:
|
|
740
|
+
default: D(() => [b(t.$slots, "default")]),
|
|
624
741
|
_: 3
|
|
625
742
|
}, 8, ["as", "data-padding"]));
|
|
626
743
|
}
|
|
627
|
-
})
|
|
744
|
+
}), qe = ({ targetIds: e }) => {
|
|
745
|
+
let t = v(e[0]), n, r, i;
|
|
746
|
+
return p(() => {
|
|
747
|
+
let a = e.flatMap((e) => {
|
|
748
|
+
let t = document.getElementById(e);
|
|
749
|
+
return t ? [{
|
|
750
|
+
id: e,
|
|
751
|
+
element: t
|
|
752
|
+
}] : [];
|
|
753
|
+
}), o = () => {
|
|
754
|
+
n = void 0;
|
|
755
|
+
let e = document.documentElement, r = Number.parseFloat(getComputedStyle(e).scrollPaddingTop) || 0, i = a[0]?.id;
|
|
756
|
+
for (let e of a) e.element.getBoundingClientRect().top <= r + 1 && (i = e.id);
|
|
757
|
+
window.scrollY > 0 && window.scrollY + window.innerHeight >= e.scrollHeight - 2 && (i = a.at(-1)?.id), t.value = i;
|
|
758
|
+
}, s = () => {
|
|
759
|
+
n === void 0 && (n = requestAnimationFrame(o));
|
|
760
|
+
};
|
|
761
|
+
window.addEventListener("scroll", s, { passive: !0 }), window.addEventListener("resize", s), window.addEventListener("pageshow", s), document.body.addEventListener("transitionend", s), typeof ResizeObserver < "u" && (r = new ResizeObserver(s), r.observe(document.body)), i = () => {
|
|
762
|
+
window.removeEventListener("scroll", s), window.removeEventListener("resize", s), window.removeEventListener("pageshow", s), document.body.removeEventListener("transitionend", s);
|
|
763
|
+
}, o();
|
|
764
|
+
}), m(() => {
|
|
765
|
+
i?.(), r?.disconnect(), n !== void 0 && cancelAnimationFrame(n);
|
|
766
|
+
}), _(t);
|
|
767
|
+
};
|
|
628
768
|
//#endregion
|
|
629
|
-
export {
|
|
769
|
+
export { ge as UiAlert, _e as UiBadge, q as UiButton, ie as UiCard, be as UiCheckbox, he as UiCodeBlock, we as UiDialog, De as UiDrawer, Me as UiField, Ne as UiInput, $ as UiProgress, Ie as UiRadioCard, Le as UiRadioCardGroup, Re as UiSelect, ze as UiSlider, Ke as UiSurface, Ge as UiTabs, qe as useActiveSection };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Ref } from 'vue';
|
|
2
|
+
export interface ActiveSectionOptions {
|
|
3
|
+
/** Section IDs in page order. Missing elements are ignored after mounting. */
|
|
4
|
+
targetIds: readonly string[];
|
|
5
|
+
}
|
|
6
|
+
/** Tracks the last section heading above the document's scroll-padding inset. */
|
|
7
|
+
export declare const useActiveSection: ({ targetIds }: ActiveSectionOptions) => Readonly<Ref<string | undefined>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@filipgutica/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A compact Vue design system with Tailwind CSS and VS Code theme support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"dist",
|
|
11
11
|
"src/theme.css",
|
|
12
12
|
"src/components.css",
|
|
13
|
+
"src/components/*.css",
|
|
13
14
|
"README.md",
|
|
14
15
|
"LICENSE"
|
|
15
16
|
],
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
.fg-drawer__overlay {
|
|
3
|
+
position: fixed;
|
|
4
|
+
inset: 0;
|
|
5
|
+
z-index: 50;
|
|
6
|
+
background: var(--color-overlay);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.fg-drawer__content {
|
|
10
|
+
position: fixed;
|
|
11
|
+
inset-block: 0;
|
|
12
|
+
left: 0;
|
|
13
|
+
z-index: 51;
|
|
14
|
+
display: grid;
|
|
15
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
16
|
+
width: min(calc(var(--sidebar) + var(--space-12)), calc(100vw - var(--space-12)));
|
|
17
|
+
height: 100vh;
|
|
18
|
+
height: 100dvh;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
border-right: 1px solid var(--color-border-subtle);
|
|
21
|
+
color: var(--color-text);
|
|
22
|
+
background: var(--color-surface-raised);
|
|
23
|
+
box-shadow: var(--shadow-dialog);
|
|
24
|
+
font-family: var(--font-ui);
|
|
25
|
+
font-size: var(--text-md);
|
|
26
|
+
font-weight: var(--weight-regular);
|
|
27
|
+
line-height: var(--leading);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.fg-drawer__header {
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: space-between;
|
|
34
|
+
gap: var(--space-3);
|
|
35
|
+
padding: var(--space-3) var(--space-4);
|
|
36
|
+
border-bottom: 1px solid var(--color-border-subtle);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.fg-drawer__title {
|
|
40
|
+
min-width: 0;
|
|
41
|
+
margin: 0;
|
|
42
|
+
font-size: var(--text-md);
|
|
43
|
+
font-weight: var(--weight-medium);
|
|
44
|
+
overflow-wrap: anywhere;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.fg-drawer__close {
|
|
48
|
+
display: inline-grid;
|
|
49
|
+
flex: none;
|
|
50
|
+
min-width: var(--control-lg);
|
|
51
|
+
min-height: var(--control-lg);
|
|
52
|
+
padding: 0;
|
|
53
|
+
place-items: center;
|
|
54
|
+
border: 0;
|
|
55
|
+
border-radius: var(--radius-control);
|
|
56
|
+
color: var(--color-muted);
|
|
57
|
+
background: transparent;
|
|
58
|
+
font: inherit;
|
|
59
|
+
font-size: var(--text-xl);
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.fg-drawer__close:hover {
|
|
64
|
+
color: var(--color-text);
|
|
65
|
+
background: var(--color-row-hover);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.fg-drawer__body {
|
|
69
|
+
min-height: 0;
|
|
70
|
+
overflow: auto;
|
|
71
|
+
overscroll-behavior: contain;
|
|
72
|
+
padding: var(--space-4);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.fg-drawer__close:focus-visible,
|
|
76
|
+
.fg-drawer__content:focus-visible {
|
|
77
|
+
outline: 2px solid var(--color-focus);
|
|
78
|
+
outline-offset: 2px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.fg-drawer__overlay[data-state="open"] { animation: fg-drawer-fade-in var(--duration) var(--ease-out); }
|
|
82
|
+
.fg-drawer__overlay[data-state="closed"] { animation: fg-drawer-fade-out var(--duration-fast) var(--ease-out); }
|
|
83
|
+
.fg-drawer__content[data-state="open"] { animation: fg-drawer-slide-in var(--duration) var(--ease-out); }
|
|
84
|
+
.fg-drawer__content[data-state="closed"] { animation: fg-drawer-slide-out var(--duration-fast) var(--ease-out); }
|
|
85
|
+
|
|
86
|
+
@keyframes fg-drawer-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
87
|
+
@keyframes fg-drawer-fade-out { from { opacity: 1; } to { opacity: 0; } }
|
|
88
|
+
@keyframes fg-drawer-slide-in { from { transform: translateX(-100%); } to { transform: translateX(0); } }
|
|
89
|
+
@keyframes fg-drawer-slide-out { from { transform: translateX(0); } to { transform: translateX(-100%); } }
|
|
90
|
+
|
|
91
|
+
:root.high-contrast .fg-drawer__content {
|
|
92
|
+
border-width: 2px;
|
|
93
|
+
border-color: var(--color-border);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@media (prefers-reduced-motion: reduce) {
|
|
97
|
+
.fg-drawer__overlay,
|
|
98
|
+
.fg-drawer__content { animation: none; }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
.fg-tabs {
|
|
3
|
+
min-width: 0;
|
|
4
|
+
font-family: var(--font-ui);
|
|
5
|
+
font-size: var(--text-md);
|
|
6
|
+
line-height: var(--leading);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.fg-tabs__list {
|
|
10
|
+
display: flex;
|
|
11
|
+
max-width: 100%;
|
|
12
|
+
overflow-x: auto;
|
|
13
|
+
border-bottom: 1px solid var(--color-border-subtle);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.fg-tabs__tab {
|
|
17
|
+
flex: none;
|
|
18
|
+
min-height: var(--control-md);
|
|
19
|
+
padding: var(--space-2) var(--space-3);
|
|
20
|
+
border: 0;
|
|
21
|
+
border-bottom: 2px solid transparent;
|
|
22
|
+
color: var(--color-muted);
|
|
23
|
+
background: transparent;
|
|
24
|
+
font: inherit;
|
|
25
|
+
white-space: nowrap;
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.fg-tabs__tab:hover { color: var(--color-text); }
|
|
30
|
+
.fg-tabs__tab[aria-selected="true"] {
|
|
31
|
+
border-bottom-color: var(--color-link);
|
|
32
|
+
color: var(--color-text);
|
|
33
|
+
font-weight: var(--weight-medium);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.fg-tabs__panel { min-width: 0; }
|
|
37
|
+
.fg-tabs__panel[hidden], .fg-tabs__heading[hidden] { display: none; }
|
|
38
|
+
.fg-tabs__heading {
|
|
39
|
+
margin-block: var(--space-4) var(--space-2);
|
|
40
|
+
font-size: var(--text-md);
|
|
41
|
+
font-weight: var(--weight-medium);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.fg-tabs__tab:focus-visible, .fg-tabs__panel:focus-visible {
|
|
45
|
+
outline: 2px solid var(--color-focus);
|
|
46
|
+
outline-offset: -2px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@media (max-width: 640px) {
|
|
50
|
+
.fg-tabs__tab { min-height: var(--control-lg); }
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/components.css
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
@import './components/drawer.css';
|
|
2
|
+
@import './components/tabs.css';
|
|
3
|
+
|
|
1
4
|
@layer components {
|
|
2
5
|
/* Package-owned typography keeps controls consistent across consuming apps. */
|
|
3
6
|
.fg-button, .fg-input, .fg-select, .fg-checkbox, .fg-field,
|
|
@@ -34,7 +37,10 @@
|
|
|
34
37
|
.fg-code-block__copy {
|
|
35
38
|
display: inline-flex;
|
|
36
39
|
align-items: center;
|
|
40
|
+
justify-content: center;
|
|
37
41
|
gap: 0.4rem;
|
|
42
|
+
min-width: var(--control-lg);
|
|
43
|
+
min-height: var(--control-lg);
|
|
38
44
|
padding: 0.25rem 0.4rem;
|
|
39
45
|
border: 0;
|
|
40
46
|
border-radius: 0.25rem;
|