@marianmeres/stuic 3.115.0 → 3.117.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/API.md +297 -304
- package/dist/actions/dim-behind/index.css +4 -1
- package/dist/actions/focus-trap.fixture.svelte +16 -0
- package/dist/actions/focus-trap.fixture.svelte.d.ts +7 -0
- package/dist/actions/focus-trap.js +3 -1
- package/dist/components/Accordion/README.md +17 -17
- package/dist/components/Accordion/index.css +4 -2
- package/dist/components/AssetsPreview/README.md +7 -7
- package/dist/components/AssetsPreview/_internal/assets-preview-types.d.ts +1 -2
- package/dist/components/AssetsPreview/_internal/assets-preview-utils.d.ts +1 -1
- package/dist/components/AssetsPreview/_internal/assets-preview-utils.js +9 -3
- package/dist/components/Avatar/Avatar.svelte +1 -3
- package/dist/components/Avatar/README.md +33 -27
- package/dist/components/Book/Book.svelte +6 -1
- package/dist/components/Book/README.md +22 -20
- package/dist/components/Book/index.css +4 -2
- package/dist/components/Button/README.md +17 -17
- package/dist/components/Card/Card.svelte +25 -8
- package/dist/components/Card/README.md +52 -56
- package/dist/components/Card/index.css +2 -1
- package/dist/components/Carousel/Carousel.svelte +1 -3
- package/dist/components/Carousel/README.md +28 -28
- package/dist/components/Cart/Cart.svelte +2 -1
- package/dist/components/Cart/README.md +25 -25
- package/dist/components/Checkout/CheckoutGuestOrLoginForm.svelte +8 -3
- package/dist/components/Checkout/CheckoutShippingStep.svelte +1 -2
- package/dist/components/Checkout/README.md +143 -130
- package/dist/components/CommandMenu/CommandMenu.fixture.svelte +24 -0
- package/dist/components/CommandMenu/CommandMenu.fixture.svelte.d.ts +7 -0
- package/dist/components/CommandMenu/CommandMenu.svelte +10 -13
- package/dist/components/CommandMenu/_internal/command-menu-utils.d.ts +22 -0
- package/dist/components/CommandMenu/_internal/command-menu-utils.js +37 -0
- package/dist/components/CronInput/CronInput.svelte +64 -60
- package/dist/components/CronInput/README.md +46 -46
- package/dist/components/DataTable/DataTable.svelte +5 -1
- package/dist/components/DataTable/README.md +78 -63
- package/dist/components/DropdownMenu/DropdownMenu.svelte +14 -29
- package/dist/components/DropdownMenu/README.md +33 -27
- package/dist/components/DropdownMenu/_internal/dropdown-menu-search.d.ts +21 -0
- package/dist/components/DropdownMenu/_internal/dropdown-menu-search.js +47 -0
- package/dist/components/EmailVerifyForm/EmailVerifyForm.svelte +2 -9
- package/dist/components/EmailVerifyForm/README.md +30 -30
- package/dist/components/Header/Header.svelte +161 -165
- package/dist/components/Header/README.md +7 -7
- package/dist/components/IconSwap/README.md +20 -15
- package/dist/components/IconSwap/index.css +2 -1
- package/dist/components/ImageCycler/ImageCycler.svelte +19 -5
- package/dist/components/ImageCycler/ImageCycler.svelte.d.ts +14 -10
- package/dist/components/ImageCycler/README.md +15 -15
- package/dist/components/ImageCycler/index.css +26 -20
- package/dist/components/Input/FieldFile.svelte +1 -3
- package/dist/components/Input/FieldInput.svelte +1 -3
- package/dist/components/Input/FieldKeyValues.svelte +2 -6
- package/dist/components/Input/FieldObject.svelte +2 -1
- package/dist/components/Input/README.md +11 -11
- package/dist/components/Input/node_modules/.vite/vitest/d2a04d71301a8915217dd5faf81d12cffd6cd958/_svelte_metadata.json +1 -0
- package/dist/components/Input/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/_svelte_metadata.json +1 -0
- package/dist/components/KbdShortcut/index.css +2 -1
- package/dist/components/LoginForm/LoginForm.svelte +1 -7
- package/dist/components/LoginForm/README.md +46 -46
- package/dist/components/ModalDialog/ModalDialog.fixture.svelte +19 -0
- package/dist/components/ModalDialog/ModalDialog.fixture.svelte.d.ts +4 -0
- package/dist/components/ModalDialog/index.css +2 -1
- package/dist/components/Notifications/index.css +24 -6
- package/dist/components/OtpInput/OtpInput.svelte +0 -0
- package/dist/components/OtpInput/README.md +15 -19
- package/dist/components/OtpInput/index.css +1 -4
- package/dist/components/OtpInput/index.d.ts +1 -1
- package/dist/components/OtpInput/index.js +1 -1
- package/dist/components/Pill/README.md +41 -40
- package/dist/components/Pill/index.css +3 -6
- package/dist/components/PricingTable/README.md +86 -86
- package/dist/components/PricingTable/index.css +20 -35
- package/dist/components/RegisterForm/README.md +60 -60
- package/dist/components/RegisterForm/RegisterForm.svelte +1 -7
- package/dist/components/Separator/README.md +7 -7
- package/dist/components/SlidingPanels/SlidingPanels.fixture.svelte +20 -0
- package/dist/components/SlidingPanels/SlidingPanels.fixture.svelte.d.ts +6 -0
- package/dist/components/TabbedMenu/index.css +6 -3
- package/dist/components/Tree/README.md +67 -67
- package/dist/components/UserAvatarMenu/UserAvatarMenu.svelte +1 -5
- package/dist/components/WithSidePanel/index.css +4 -4
- package/dist/index.css +12 -8
- package/dist/utils/design-tokens.d.ts +1 -1
- package/dist/utils/design-tokens.js +1 -1
- package/docs/architecture.md +7 -7
- package/docs/component-testing/00-overview-and-roadmap.md +19 -19
- package/docs/component-testing/01-framework-setup.md +6 -6
- package/docs/component-testing/02-test-conventions.md +6 -5
- package/docs/component-testing/03-component-coverage-roadmap.md +27 -27
- package/docs/component-testing/04-hard-cases-and-e2e.md +8 -8
- package/docs/component-testing/05-ci.md +3 -3
- package/docs/component-testing/PROGRESS.md +118 -26
- package/docs/component-testing/README.md +8 -8
- package/docs/conventions.md +25 -25
- package/docs/domains/components.md +386 -385
- package/docs/domains/theming.md +24 -24
- package/docs/domains/utils.md +22 -25
- package/docs/testing.md +2 -2
- package/docs/upgrading.md +32 -28
- package/package.json +2 -1
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
background: var(--stuic-dim-behind-backdrop-bg);
|
|
17
17
|
opacity: 0;
|
|
18
18
|
transition-property: opacity;
|
|
19
|
-
transition-duration: var(
|
|
19
|
+
transition-duration: var(
|
|
20
|
+
--stuic-dim-behind-transition-duration,
|
|
21
|
+
var(--stuic-transition)
|
|
22
|
+
);
|
|
20
23
|
pointer-events: auto;
|
|
21
24
|
|
|
22
25
|
&.dim-visible {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { focusTrap } from "./focus-trap.js";
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
enabled = true,
|
|
6
|
+
autoFocusFirst = true,
|
|
7
|
+
}: { enabled?: boolean; autoFocusFirst?: boolean } = $props();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<!-- A minimal focusable set to exercise the trap's auto-focus + Tab wrap-around. -->
|
|
11
|
+
<div use:focusTrap={{ enabled, autoFocusFirst }} data-testid="trap">
|
|
12
|
+
<button data-testid="first">First</button>
|
|
13
|
+
<input data-testid="middle" type="text" />
|
|
14
|
+
<button data-testid="last">Last</button>
|
|
15
|
+
</div>
|
|
16
|
+
<button data-testid="outside">Outside</button>
|
|
@@ -39,7 +39,9 @@ const defaults = { enabled: true, autoFocusFirst: true };
|
|
|
39
39
|
* ```
|
|
40
40
|
*/
|
|
41
41
|
export function focusTrap(node, options = {}) {
|
|
42
|
-
|
|
42
|
+
const merged = { ...defaults, ...(options ?? {}) };
|
|
43
|
+
let { enabled = true } = merged; // reassigned by the update hook below
|
|
44
|
+
const { autoFocusFirst } = merged;
|
|
43
45
|
const focusableSelectors = [
|
|
44
46
|
"[contentEditable=true]",
|
|
45
47
|
//
|
|
@@ -93,21 +93,21 @@ Only one item can be open at a time:
|
|
|
93
93
|
|
|
94
94
|
## CSS Variables
|
|
95
95
|
|
|
96
|
-
| Variable | Default | Description
|
|
97
|
-
| --------------------------------------- | ---------------------- |
|
|
98
|
-
| `--stuic-accordion-border-color` | `--stuic-color-border` | Item separator color
|
|
99
|
-
| `--stuic-accordion-border-width` | `1px` | Separator width
|
|
96
|
+
| Variable | Default | Description |
|
|
97
|
+
| --------------------------------------- | ---------------------- | --------------------------------------------- |
|
|
98
|
+
| `--stuic-accordion-border-color` | `--stuic-color-border` | Item separator color |
|
|
99
|
+
| `--stuic-accordion-border-width` | `1px` | Separator width |
|
|
100
100
|
| `--stuic-accordion-divider-inset` | `0` | Inset of divider from L/R edges (e.g. `16px`) |
|
|
101
|
-
| `--stuic-accordion-radius` | `--radius-md` | Corner rounding
|
|
102
|
-
| `--stuic-accordion-transition` | `200ms` | Open/close animation duration
|
|
103
|
-
| `--stuic-accordion-trigger-padding-x` | `1rem` | Trigger horizontal padding
|
|
104
|
-
| `--stuic-accordion-trigger-padding-y` | `0.75rem` | Trigger vertical padding
|
|
105
|
-
| `--stuic-accordion-trigger-font-weight` | `--font-weight-medium` | Trigger text weight
|
|
106
|
-
| `--stuic-accordion-trigger-bg` | `transparent` | Trigger background
|
|
107
|
-
| `--stuic-accordion-trigger-bg-hover` | muted mix | Trigger hover background
|
|
108
|
-
| `--stuic-accordion-chevron-size` | `16px` | Chevron icon size
|
|
109
|
-
| `--stuic-accordion-chevron-color` | `currentColor` | Chevron icon color
|
|
110
|
-
| `--stuic-accordion-content-padding-x` | `1rem` | Content horizontal padding
|
|
111
|
-
| `--stuic-accordion-content-padding-y` | `0.75rem` | Content vertical padding
|
|
112
|
-
| `--stuic-accordion-ring-width` | `2px` | Focus ring width
|
|
113
|
-
| `--stuic-accordion-ring-color` | `--stuic-color-ring` | Focus ring color
|
|
101
|
+
| `--stuic-accordion-radius` | `--radius-md` | Corner rounding |
|
|
102
|
+
| `--stuic-accordion-transition` | `200ms` | Open/close animation duration |
|
|
103
|
+
| `--stuic-accordion-trigger-padding-x` | `1rem` | Trigger horizontal padding |
|
|
104
|
+
| `--stuic-accordion-trigger-padding-y` | `0.75rem` | Trigger vertical padding |
|
|
105
|
+
| `--stuic-accordion-trigger-font-weight` | `--font-weight-medium` | Trigger text weight |
|
|
106
|
+
| `--stuic-accordion-trigger-bg` | `transparent` | Trigger background |
|
|
107
|
+
| `--stuic-accordion-trigger-bg-hover` | muted mix | Trigger hover background |
|
|
108
|
+
| `--stuic-accordion-chevron-size` | `16px` | Chevron icon size |
|
|
109
|
+
| `--stuic-accordion-chevron-color` | `currentColor` | Chevron icon color |
|
|
110
|
+
| `--stuic-accordion-content-padding-x` | `1rem` | Content horizontal padding |
|
|
111
|
+
| `--stuic-accordion-content-padding-y` | `0.75rem` | Content vertical padding |
|
|
112
|
+
| `--stuic-accordion-ring-width` | `2px` | Focus ring width |
|
|
113
|
+
| `--stuic-accordion-ring-color` | `--stuic-color-ring` | Focus ring color |
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
============================================================================ */
|
|
42
42
|
|
|
43
43
|
.stuic-accordion {
|
|
44
|
-
border: var(--stuic-accordion-border-width, var(--stuic-border-width)) solid
|
|
44
|
+
border: var(--stuic-accordion-border-width, var(--stuic-border-width)) solid
|
|
45
|
+
var(--stuic-accordion-border-color);
|
|
45
46
|
border-radius: var(--stuic-accordion-radius, var(--stuic-radius-container));
|
|
46
47
|
overflow: hidden;
|
|
47
48
|
}
|
|
@@ -114,7 +115,8 @@
|
|
|
114
115
|
.stuic-accordion-content {
|
|
115
116
|
display: grid;
|
|
116
117
|
grid-template-rows: 0fr;
|
|
117
|
-
transition: grid-template-rows
|
|
118
|
+
transition: grid-template-rows
|
|
119
|
+
var(--stuic-accordion-transition, var(--stuic-transition));
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
[data-open="true"] > .stuic-accordion-content {
|
|
@@ -4,13 +4,13 @@ A modal-based asset preview component for displaying images and files. Supports
|
|
|
4
4
|
|
|
5
5
|
## Props
|
|
6
6
|
|
|
7
|
-
| Prop
|
|
8
|
-
|
|
|
9
|
-
| `assets`
|
|
10
|
-
| `classControls`
|
|
11
|
-
| `t`
|
|
12
|
-
| `onDelete`
|
|
13
|
-
| `prevNextBottom
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
| ---------------- | ---------------------------- | -------- | --------------------------------- |
|
|
9
|
+
| `assets` | `string[] \| AssetPreview[]` | - | Array of assets to preview |
|
|
10
|
+
| `classControls` | `string` | - | CSS for control buttons |
|
|
11
|
+
| `t` | `TranslateFn` | built-in | Translation function for i18n |
|
|
12
|
+
| `onDelete` | `(asset, index) => void` | - | Optional delete handler |
|
|
13
|
+
| `prevNextBottom` | `boolean` | `false` | Render prev/next arrows at bottom |
|
|
14
14
|
|
|
15
15
|
## Types
|
|
16
16
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AssetPreview, AssetPreviewNormalized } from "./assets-preview-types.js";
|
|
2
2
|
export declare function getAssetIcon(ext?: string): CallableFunction;
|
|
3
|
-
export declare function t_default(k: string, values?: false | null | undefined | Record<string, string | number>, fallback?: string | boolean,
|
|
3
|
+
export declare function t_default(k: string, values?: false | null | undefined | Record<string, string | number>, fallback?: string | boolean, _i18nSpanWrap?: boolean): string;
|
|
4
4
|
export declare function ext(name: string): string;
|
|
5
5
|
export declare function normalizeInput(input: string | AssetPreview): AssetPreviewNormalized | null;
|
|
@@ -21,7 +21,9 @@ export function getAssetIcon(ext) {
|
|
|
21
21
|
return map[getFileTypeLabel(ext ?? "unknown")] ?? iconFile;
|
|
22
22
|
}
|
|
23
23
|
// i18n ready
|
|
24
|
-
export function t_default(k, values = null, fallback = "",
|
|
24
|
+
export function t_default(k, values = null, fallback = "",
|
|
25
|
+
// kept for signature compatibility; not used by this default impl
|
|
26
|
+
_i18nSpanWrap = true) {
|
|
25
27
|
const m = {
|
|
26
28
|
unable_to_preview: "Unable to preview",
|
|
27
29
|
download: "Download",
|
|
@@ -30,8 +32,12 @@ export function t_default(k, values = null, fallback = "", i18nSpanWrap = true)
|
|
|
30
32
|
zoom_out: "Zoom out",
|
|
31
33
|
delete: "Delete",
|
|
32
34
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
const out = m[k] ?? fallback ?? k;
|
|
36
|
+
// values is narrowed to Record<string, string | number>; replaceMap wants
|
|
37
|
+
// string | CallableFunction values (it stringifies), hence the bridge cast.
|
|
38
|
+
return isPlainObject(values)
|
|
39
|
+
? replaceMap(out, values)
|
|
40
|
+
: out;
|
|
35
41
|
}
|
|
36
42
|
// naive best-effort
|
|
37
43
|
export function ext(name) {
|
|
@@ -180,9 +180,7 @@
|
|
|
180
180
|
);
|
|
181
181
|
|
|
182
182
|
// When padded: colors go on inner element; otherwise on outer
|
|
183
|
-
let outerStyle = $derived(
|
|
184
|
-
padding ? `--stuic-avatar-padding: ${padding}` : colorStyle
|
|
185
|
-
);
|
|
183
|
+
let outerStyle = $derived(padding ? `--stuic-avatar-padding: ${padding}` : colorStyle);
|
|
186
184
|
let innerStyle = $derived(padding ? colorStyle : undefined);
|
|
187
185
|
|
|
188
186
|
// Build class string - base class for CSS targeting, allow user overrides via classProp
|
|
@@ -4,24 +4,24 @@ A flexible avatar component that displays user photos, initials, or icons with a
|
|
|
4
4
|
|
|
5
5
|
## Props
|
|
6
6
|
|
|
7
|
-
| Prop | Type | Default | Description
|
|
8
|
-
| ---------------- | ------------------------------------------------- | -------- |
|
|
9
|
-
| `src` | `string` | - | Photo URL - when provided, renders in photo mode
|
|
10
|
-
| `alt` | `string` | - | Alt text for photo mode
|
|
11
|
-
| `initials` | `string` | - | String to extract initials from. Supports: "AB", "John Doe", or "john.doe@example.com"
|
|
12
|
-
| `initialsLength` | `number` | `2` | Maximum length of extracted initials
|
|
13
|
-
| `icon` | `IconFn` | - | Icon function to display - when provided alone, renders in icon mode
|
|
14
|
-
| `fallback` | `AvatarFallback` | `"icon"` | Fallback when photo fails to load
|
|
15
|
-
| `hashSource` | `string` | - | String for color hash calculation (e.g., email, user ID). Falls back to `initials`
|
|
16
|
-
| `size` | `"sm" \| "md" \| "lg" \| "xl" \| "2xl" \| string` | `"md"` | Size preset or custom Tailwind size class
|
|
17
|
-
| `onclick` | `(event: MouseEvent) => void` | - | Click handler - when provided, renders as a button
|
|
18
|
-
| `bg` | `string` | - | Background color (Tailwind class). Ignored if `autoColor=true`
|
|
19
|
-
| `textColor` | `string` | - | Text color (Tailwind class). Ignored if `autoColor=true`
|
|
20
|
-
| `autoColor` | `boolean` | `false` | Generate deterministic pastel colors from hashSource/initials
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
| ---------------- | ------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| `src` | `string` | - | Photo URL - when provided, renders in photo mode |
|
|
10
|
+
| `alt` | `string` | - | Alt text for photo mode |
|
|
11
|
+
| `initials` | `string` | - | String to extract initials from. Supports: "AB", "John Doe", or "john.doe@example.com" |
|
|
12
|
+
| `initialsLength` | `number` | `2` | Maximum length of extracted initials |
|
|
13
|
+
| `icon` | `IconFn` | - | Icon function to display - when provided alone, renders in icon mode |
|
|
14
|
+
| `fallback` | `AvatarFallback` | `"icon"` | Fallback when photo fails to load |
|
|
15
|
+
| `hashSource` | `string` | - | String for color hash calculation (e.g., email, user ID). Falls back to `initials` |
|
|
16
|
+
| `size` | `"sm" \| "md" \| "lg" \| "xl" \| "2xl" \| string` | `"md"` | Size preset or custom Tailwind size class |
|
|
17
|
+
| `onclick` | `(event: MouseEvent) => void` | - | Click handler - when provided, renders as a button |
|
|
18
|
+
| `bg` | `string` | - | Background color (Tailwind class). Ignored if `autoColor=true` |
|
|
19
|
+
| `textColor` | `string` | - | Text color (Tailwind class). Ignored if `autoColor=true` |
|
|
20
|
+
| `autoColor` | `boolean` | `false` | Generate deterministic pastel colors from hashSource/initials |
|
|
21
21
|
| `padding` | `string` | - | CSS padding around the visual circle. Outer keeps its size, inner circle shrinks (e.g. `"4px"`, `"0.25rem"`). Useful for larger tap targets. |
|
|
22
|
-
| `class` | `string` | - | Additional CSS classes
|
|
23
|
-
| `classInner` | `string` | - | Additional CSS classes for the inner element (only used when `padding` is set)
|
|
24
|
-
| `el` | `HTMLElement` | - | Bindable element reference
|
|
22
|
+
| `class` | `string` | - | Additional CSS classes |
|
|
23
|
+
| `classInner` | `string` | - | Additional CSS classes for the inner element (only used when `padding` is set) |
|
|
24
|
+
| `el` | `HTMLElement` | - | Bindable element reference |
|
|
25
25
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
@@ -91,7 +91,13 @@ Use `padding` to keep the avatar's outer footprint (e.g. a 44px tap target) whil
|
|
|
91
91
|
```svelte
|
|
92
92
|
<!-- Outer stays at size="md" (2.75rem), circle shrinks by 4px on each side -->
|
|
93
93
|
<Avatar size="md" initials="AB" padding="4px" />
|
|
94
|
-
<Avatar
|
|
94
|
+
<Avatar
|
|
95
|
+
size="md"
|
|
96
|
+
autoColor
|
|
97
|
+
initials="john@example.com"
|
|
98
|
+
padding="0.25rem"
|
|
99
|
+
onclick={() => {}}
|
|
100
|
+
/>
|
|
95
101
|
```
|
|
96
102
|
|
|
97
103
|
When `padding` is set, `bg`, `textColor`, and `autoColor` automatically apply to the inner circle (not the outer element).
|
|
@@ -112,15 +118,15 @@ When `padding` is set, `bg`, `textColor`, and `autoColor` automatically apply to
|
|
|
112
118
|
|
|
113
119
|
Override globally in `:root` or locally via `style` prop:
|
|
114
120
|
|
|
115
|
-
| Variable | Default | Description
|
|
116
|
-
| ---------------------------- | -------------------------------- |
|
|
117
|
-
| `--stuic-avatar-radius` | `9999px` | Border radius (circle by default)
|
|
118
|
-
| `--stuic-avatar-font-weight` | `--font-weight-medium` | Font weight for initials
|
|
119
|
-
| `--stuic-avatar-transition` | `150ms` | Transition duration
|
|
120
|
-
| `--stuic-avatar-bg` | `--stuic-color-muted` | Default background color
|
|
121
|
-
| `--stuic-avatar-fg` | `--stuic-color-muted-foreground` | Default text/icon color
|
|
122
|
-
| `--stuic-avatar-ring-width` | `3px` | Focus ring width (button mode)
|
|
123
|
-
| `--stuic-avatar-ring-color` | `--stuic-color-ring` | Focus ring color
|
|
121
|
+
| Variable | Default | Description |
|
|
122
|
+
| ---------------------------- | -------------------------------- | -------------------------------------------------------------- |
|
|
123
|
+
| `--stuic-avatar-radius` | `9999px` | Border radius (circle by default) |
|
|
124
|
+
| `--stuic-avatar-font-weight` | `--font-weight-medium` | Font weight for initials |
|
|
125
|
+
| `--stuic-avatar-transition` | `150ms` | Transition duration |
|
|
126
|
+
| `--stuic-avatar-bg` | `--stuic-color-muted` | Default background color |
|
|
127
|
+
| `--stuic-avatar-fg` | `--stuic-color-muted-foreground` | Default text/icon color |
|
|
128
|
+
| `--stuic-avatar-ring-width` | `3px` | Focus ring width (button mode) |
|
|
129
|
+
| `--stuic-avatar-ring-color` | `--stuic-color-ring` | Focus ring color |
|
|
124
130
|
| `--stuic-avatar-padding` | - | Set by the `padding` prop; can also be driven directly via CSS |
|
|
125
131
|
|
|
126
132
|
### Size Tokens
|
|
@@ -720,7 +720,12 @@
|
|
|
720
720
|
|
|
721
721
|
{#if spreads.length}
|
|
722
722
|
<!-- Measurement wrapper: always 100% width for responsive detection -->
|
|
723
|
-
<div
|
|
723
|
+
<div
|
|
724
|
+
bind:clientWidth={containerWidth}
|
|
725
|
+
style:width="100%"
|
|
726
|
+
style:display="flex"
|
|
727
|
+
style:justify-content="center"
|
|
728
|
+
>
|
|
724
729
|
<!-- Visual book wrapper: owns background/shadow/radius -->
|
|
725
730
|
<div
|
|
726
731
|
bind:this={el}
|
|
@@ -22,7 +22,9 @@ A physical book visualization with CSS 3D page-flipping animation. Displays an o
|
|
|
22
22
|
let activeSpread = $state(0);
|
|
23
23
|
</script>
|
|
24
24
|
|
|
25
|
-
<div
|
|
25
|
+
<div
|
|
26
|
+
style="--stuic-book-page-width: {size.width}px; --stuic-book-page-height: {size.height}px;"
|
|
27
|
+
>
|
|
26
28
|
<Book bind:this={book} {pages} bind:activeSpread />
|
|
27
29
|
</div>
|
|
28
30
|
|
|
@@ -68,10 +70,10 @@ Pages are grouped into **spreads**:
|
|
|
68
70
|
```typescript
|
|
69
71
|
interface BookPageArea {
|
|
70
72
|
id: string | number;
|
|
71
|
-
x: number;
|
|
72
|
-
y: number;
|
|
73
|
-
w: number;
|
|
74
|
-
h: number;
|
|
73
|
+
x: number; // X position in natural image pixels
|
|
74
|
+
y: number; // Y position in natural image pixels
|
|
75
|
+
w: number; // Width in natural image pixels
|
|
76
|
+
h: number; // Height in natural image pixels
|
|
75
77
|
[key: string]: any;
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -79,8 +81,8 @@ interface BookPage {
|
|
|
79
81
|
id: string | number;
|
|
80
82
|
src: string;
|
|
81
83
|
title?: string;
|
|
82
|
-
width: number;
|
|
83
|
-
height: number;
|
|
84
|
+
width: number; // Natural image width in px
|
|
85
|
+
height: number; // Natural image height in px
|
|
84
86
|
areas?: BookPageArea[];
|
|
85
87
|
[key: string]: any;
|
|
86
88
|
}
|
|
@@ -114,8 +116,8 @@ Computes display dimensions from page image metadata. Finds the largest width/he
|
|
|
114
116
|
```ts
|
|
115
117
|
import { computeBookPageSize } from "@marianmeres/stuic";
|
|
116
118
|
|
|
117
|
-
const size = computeBookPageSize(pages);
|
|
118
|
-
const size = computeBookPageSize(pages, 300);
|
|
119
|
+
const size = computeBookPageSize(pages); // { width: 283, height: 400 }
|
|
120
|
+
const size = computeBookPageSize(pages, 300); // { width: 212, height: 300 }
|
|
119
121
|
```
|
|
120
122
|
|
|
121
123
|
## Clickable Areas
|
|
@@ -155,14 +157,14 @@ Pages can define clickable areas (e.g. product hotspots in a catalog). Areas are
|
|
|
155
157
|
|
|
156
158
|
## CSS Variables
|
|
157
159
|
|
|
158
|
-
| Variable
|
|
159
|
-
|
|
|
160
|
-
| `--stuic-book-page-width`
|
|
161
|
-
| `--stuic-book-page-height`
|
|
162
|
-
| `--stuic-book-perspective`
|
|
163
|
-
| `--stuic-book-duration`
|
|
164
|
-
| `--stuic-book-timing`
|
|
165
|
-
| `--stuic-book-page-bg`
|
|
166
|
-
| `--stuic-book-page-shadow`
|
|
167
|
-
| `--stuic-book-radius`
|
|
168
|
-
| `--stuic-book-area-fill-hover` | `rgba(0, 0, 0, 0.06)`
|
|
160
|
+
| Variable | Default | Description |
|
|
161
|
+
| ------------------------------ | ----------------------------- | ------------------------- |
|
|
162
|
+
| `--stuic-book-page-width` | `300px` | Width of a single page |
|
|
163
|
+
| `--stuic-book-page-height` | `400px` | Height of the book |
|
|
164
|
+
| `--stuic-book-perspective` | `1200px` | CSS perspective depth |
|
|
165
|
+
| `--stuic-book-duration` | `600ms` | Flip animation duration |
|
|
166
|
+
| `--stuic-book-timing` | `ease-in-out` | Animation timing function |
|
|
167
|
+
| `--stuic-book-page-bg` | `var(--stuic-color-surface)` | Page background color |
|
|
168
|
+
| `--stuic-book-page-shadow` | `0 2px 16px rgba(0,0,0,0.15)` | Book shadow |
|
|
169
|
+
| `--stuic-book-radius` | `var(--radius-sm)` | Page border radius |
|
|
170
|
+
| `--stuic-book-area-fill-hover` | `rgba(0, 0, 0, 0.06)` | Area hover highlight fill |
|
|
@@ -64,7 +64,8 @@
|
|
|
64
64
|
backface-visibility: hidden;
|
|
65
65
|
-webkit-backface-visibility: hidden;
|
|
66
66
|
background: var(--stuic-book-page-bg);
|
|
67
|
-
border-radius: 0 var(--stuic-book-radius, var(--stuic-radius))
|
|
67
|
+
border-radius: 0 var(--stuic-book-radius, var(--stuic-radius))
|
|
68
|
+
var(--stuic-book-radius, var(--stuic-radius)) 0;
|
|
68
69
|
overflow: hidden;
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -82,7 +83,8 @@
|
|
|
82
83
|
-webkit-backface-visibility: hidden;
|
|
83
84
|
transform: rotateY(180deg);
|
|
84
85
|
background: var(--stuic-book-page-bg);
|
|
85
|
-
border-radius: var(--stuic-book-radius, var(--stuic-radius)) 0 0
|
|
86
|
+
border-radius: var(--stuic-book-radius, var(--stuic-radius)) 0 0
|
|
87
|
+
var(--stuic-book-radius, var(--stuic-radius));
|
|
86
88
|
overflow: hidden;
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -4,24 +4,24 @@ A flexible button component with semantic intents, visual variants, sizes, and o
|
|
|
4
4
|
|
|
5
5
|
## Props
|
|
6
6
|
|
|
7
|
-
| Prop | Type | Default | Description
|
|
8
|
-
| ------------ | ------------------------------------------------------------------ | --------- |
|
|
9
|
-
| `intent` | `"primary" \| "accent" \| "destructive" \| "warning" \| "success"` | - | Semantic color intent
|
|
10
|
-
| `variant` | `"solid" \| "outline" \| "ghost" \| "soft" \| "link"` | `"solid"` | Visual variant (how colors are applied)
|
|
11
|
-
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | `"md"` | Button size
|
|
12
|
-
| `muted` | `boolean` | `false` | Reduce emphasis (lower opacity)
|
|
13
|
-
| `raised` | `boolean` | `false` | 3D push effect
|
|
14
|
-
| `unstyled` | `boolean` | `false` | Skip all default styling
|
|
15
|
-
| `href` | `string` | - | Render as anchor tag with this URL
|
|
16
|
-
| `roleSwitch` | `boolean` | `false` | Enable toggle/switch behavior
|
|
17
|
-
| `checked` | `boolean` | `false` | Toggle state when `roleSwitch` is true (bindable)
|
|
18
|
-
| `el` | `HTMLElement` | - | Element reference (bindable)
|
|
19
|
-
| `iconButton` | `boolean` | `false` | Icon-only button (implies aspect1, adds CSS hook)
|
|
20
|
-
| `iconSwap` | `[string \| Snippet, string \| Snippet]` | - | Two icon states with swap animation (implies iconButton)
|
|
21
|
-
| `x` | `boolean \| XProps` | - | Normalized "X" icon button shortcut (close/dismiss)
|
|
22
|
-
| `nav` | `"prev" \| "next" \| ButtonNavProps` | - | Normalized prev/next icon button shortcut (arrow by default; `x` wins on conflict)
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
| ------------ | ------------------------------------------------------------------ | --------- | ----------------------------------------------------------------------------------------- |
|
|
9
|
+
| `intent` | `"primary" \| "accent" \| "destructive" \| "warning" \| "success"` | - | Semantic color intent |
|
|
10
|
+
| `variant` | `"solid" \| "outline" \| "ghost" \| "soft" \| "link"` | `"solid"` | Visual variant (how colors are applied) |
|
|
11
|
+
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | `"md"` | Button size |
|
|
12
|
+
| `muted` | `boolean` | `false` | Reduce emphasis (lower opacity) |
|
|
13
|
+
| `raised` | `boolean` | `false` | 3D push effect |
|
|
14
|
+
| `unstyled` | `boolean` | `false` | Skip all default styling |
|
|
15
|
+
| `href` | `string` | - | Render as anchor tag with this URL |
|
|
16
|
+
| `roleSwitch` | `boolean` | `false` | Enable toggle/switch behavior |
|
|
17
|
+
| `checked` | `boolean` | `false` | Toggle state when `roleSwitch` is true (bindable) |
|
|
18
|
+
| `el` | `HTMLElement` | - | Element reference (bindable) |
|
|
19
|
+
| `iconButton` | `boolean` | `false` | Icon-only button (implies aspect1, adds CSS hook) |
|
|
20
|
+
| `iconSwap` | `[string \| Snippet, string \| Snippet]` | - | Two icon states with swap animation (implies iconButton) |
|
|
21
|
+
| `x` | `boolean \| XProps` | - | Normalized "X" icon button shortcut (close/dismiss) |
|
|
22
|
+
| `nav` | `"prev" \| "next" \| ButtonNavProps` | - | Normalized prev/next icon button shortcut (arrow by default; `x` wins on conflict) |
|
|
23
23
|
| `iconEdge` | `"leading" \| "trailing"` | - | Trim icon-side padding to the y-padding (pill + edge-flush icon; pair with `roundedFull`) |
|
|
24
|
-
| `class` | `string` | - | Additional CSS classes
|
|
24
|
+
| `class` | `string` | - | Additional CSS classes |
|
|
25
25
|
|
|
26
26
|
## Snippet Props
|
|
27
27
|
|
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
export type CardVariant = "vertical" | "horizontal";
|
|
7
7
|
|
|
8
|
-
export interface Props extends Omit<
|
|
8
|
+
export interface Props extends Omit<
|
|
9
|
+
HTMLAttributes<HTMLDivElement>,
|
|
10
|
+
"children" | "title"
|
|
11
|
+
> {
|
|
9
12
|
/** Image URL for the top (vertical) or side (horizontal) image area */
|
|
10
13
|
image?: string;
|
|
11
14
|
/** Alt text for the image */
|
|
@@ -82,14 +85,22 @@
|
|
|
82
85
|
|
|
83
86
|
let _effectiveVariant = $derived.by(() => {
|
|
84
87
|
if (variant !== "horizontal" || !horizontalThreshold) return variant;
|
|
85
|
-
return _offsetWidth > 0 && _offsetWidth < horizontalThreshold
|
|
88
|
+
return _offsetWidth > 0 && _offsetWidth < horizontalThreshold
|
|
89
|
+
? "vertical"
|
|
90
|
+
: "horizontal";
|
|
86
91
|
});
|
|
87
92
|
|
|
88
93
|
let _class = $derived(unstyled ? classProp : twMerge("stuic-card", classProp));
|
|
89
|
-
let _classImage = $derived(
|
|
90
|
-
|
|
94
|
+
let _classImage = $derived(
|
|
95
|
+
unstyled ? classImageProp : twMerge("stuic-card-image", classImageProp)
|
|
96
|
+
);
|
|
97
|
+
let _classContent = $derived(
|
|
98
|
+
unstyled ? classContentProp : twMerge("stuic-card-content", classContentProp)
|
|
99
|
+
);
|
|
91
100
|
let _classBody = $derived(unstyled ? undefined : "stuic-card-body");
|
|
92
|
-
let _classFooter = $derived(
|
|
101
|
+
let _classFooter = $derived(
|
|
102
|
+
unstyled ? classFooterProp : twMerge("stuic-card-footer", classFooterProp)
|
|
103
|
+
);
|
|
93
104
|
|
|
94
105
|
let _isInteractive = $derived(!!(href || onclick));
|
|
95
106
|
let _hasImage = $derived(!!(image || renderImage));
|
|
@@ -123,13 +134,19 @@
|
|
|
123
134
|
{:else if _hasContent}
|
|
124
135
|
<div class={_classContent}>
|
|
125
136
|
{#if eyebrow}
|
|
126
|
-
<div class={unstyled ? undefined : "stuic-card-eyebrow"}
|
|
137
|
+
<div class={unstyled ? undefined : "stuic-card-eyebrow"}>
|
|
138
|
+
<Thc thc={eyebrow} />
|
|
139
|
+
</div>
|
|
127
140
|
{/if}
|
|
128
141
|
{#if title}
|
|
129
|
-
<div class={unstyled ? undefined : "stuic-card-title"}
|
|
142
|
+
<div class={unstyled ? undefined : "stuic-card-title"}>
|
|
143
|
+
<Thc thc={title} />
|
|
144
|
+
</div>
|
|
130
145
|
{/if}
|
|
131
146
|
{#if description}
|
|
132
|
-
<div class={unstyled ? undefined : "stuic-card-description"}
|
|
147
|
+
<div class={unstyled ? undefined : "stuic-card-description"}>
|
|
148
|
+
<Thc thc={description} />
|
|
149
|
+
</div>
|
|
133
150
|
{/if}
|
|
134
151
|
</div>
|
|
135
152
|
{/if}
|