@civitai/blocks-react 0.11.2 → 0.12.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 +90 -3
- package/dist/ui/Alert.d.ts +19 -0
- package/dist/ui/Alert.d.ts.map +1 -0
- package/dist/ui/Alert.js +12 -0
- package/dist/ui/Alert.js.map +1 -0
- package/dist/ui/Badge.d.ts +18 -0
- package/dist/ui/Badge.d.ts.map +1 -0
- package/dist/ui/Badge.js +25 -0
- package/dist/ui/Badge.js.map +1 -0
- package/dist/ui/Button.d.ts +35 -0
- package/dist/ui/Button.d.ts.map +1 -0
- package/dist/ui/Button.js +44 -0
- package/dist/ui/Button.js.map +1 -0
- package/dist/ui/Card.d.ts +14 -0
- package/dist/ui/Card.d.ts.map +1 -0
- package/dist/ui/Card.js +11 -0
- package/dist/ui/Card.js.map +1 -0
- package/dist/ui/Group.d.ts +15 -0
- package/dist/ui/Group.d.ts.map +1 -0
- package/dist/ui/Group.js +22 -0
- package/dist/ui/Group.js.map +1 -0
- package/dist/ui/Loader.d.ts +18 -0
- package/dist/ui/Loader.d.ts.map +1 -0
- package/dist/ui/Loader.js +14 -0
- package/dist/ui/Loader.js.map +1 -0
- package/dist/ui/Modal.d.ts +46 -0
- package/dist/ui/Modal.d.ts.map +1 -0
- package/dist/ui/Modal.js +67 -0
- package/dist/ui/Modal.js.map +1 -0
- package/dist/ui/Stack.d.ts +13 -0
- package/dist/ui/Stack.d.ts.map +1 -0
- package/dist/ui/Stack.js +21 -0
- package/dist/ui/Stack.js.map +1 -0
- package/dist/ui/TextInput.d.ts +26 -0
- package/dist/ui/TextInput.d.ts.map +1 -0
- package/dist/ui/TextInput.js +23 -0
- package/dist/ui/TextInput.js.map +1 -0
- package/dist/ui/Textarea.d.ts +32 -0
- package/dist/ui/Textarea.d.ts.map +1 -0
- package/dist/ui/Textarea.js +23 -0
- package/dist/ui/Textarea.js.map +1 -0
- package/dist/ui/index.d.ts +33 -4
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +26 -4
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/styles.d.ts +19 -0
- package/dist/ui/styles.d.ts.map +1 -0
- package/dist/ui/styles.js +416 -0
- package/dist/ui/styles.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -208,7 +208,94 @@ track('generate_clicked', { modelId });
|
|
|
208
208
|
## The `/ui` subexport
|
|
209
209
|
|
|
210
210
|
Opinionated components, imported separately so a transport-only block stays lean.
|
|
211
|
-
|
|
211
|
+
Two surfaces live here:
|
|
212
|
+
|
|
213
|
+
1. The **W6 component pack** — a small, Civitai-looking, self-styled component
|
|
214
|
+
set you drop straight into a block.
|
|
215
|
+
2. The headless, manifest-driven **`SettingsForm`** (host-themed native controls).
|
|
216
|
+
|
|
217
|
+
### W6 component pack
|
|
218
|
+
|
|
219
|
+
A drop-in set of primitives that match Civitai's look (8px radius, the blue
|
|
220
|
+
primary, the dark/light surfaces) — **with zero setup**:
|
|
221
|
+
|
|
222
|
+
- **No Mantine dependency, no CSS import, no setup step.** The pack ships its
|
|
223
|
+
CSS as a string and injects it into your block document's `<head>` the first
|
|
224
|
+
time you render any component (idempotent). There's nothing to wire up.
|
|
225
|
+
- **First paint is briefly unstyled (FOUC).** Because the CSS injects in a
|
|
226
|
+
`useEffect` (after the first paint), the very first frame of a pack component
|
|
227
|
+
renders unstyled, then snaps to themed. It's a single frame and usually
|
|
228
|
+
unnoticeable. To eliminate it, call `injectBlocksStyles()` at module init in
|
|
229
|
+
your entry file (before the first render) so the stylesheet is present up
|
|
230
|
+
front — see `injectBlocksStyles` below (already exported).
|
|
231
|
+
- **Auto-themed via your block's `data-theme`.** Set `data-theme={theme}` on
|
|
232
|
+
your block's own root (from `useBlockContext().theme` — gotcha #60; the host
|
|
233
|
+
can't reach across the iframe to set it for you). The components read an
|
|
234
|
+
ancestor `[data-theme='dark']` / `[data-theme='light']`; **no attribute =
|
|
235
|
+
light**, matching the starter palette.
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { useRef } from 'react';
|
|
239
|
+
import { useBlockContext } from '@civitai/blocks-react';
|
|
240
|
+
import {
|
|
241
|
+
Button, TextInput, Textarea, Card, Stack, Group,
|
|
242
|
+
Alert, Loader, Badge, Modal,
|
|
243
|
+
} from '@civitai/blocks-react/ui';
|
|
244
|
+
|
|
245
|
+
export function App() {
|
|
246
|
+
const { ready, theme } = useBlockContext();
|
|
247
|
+
const rootRef = useRef<HTMLDivElement>(null);
|
|
248
|
+
if (!ready) return <div ref={rootRef}>Loading…</div>;
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
// GOTCHA #60 — theme your OWN root; that's what the pack reads.
|
|
252
|
+
<div ref={rootRef} data-theme={theme}>
|
|
253
|
+
<Card>
|
|
254
|
+
<Stack gap={12}>
|
|
255
|
+
<Group justify="space-between">
|
|
256
|
+
<strong>My block</strong>
|
|
257
|
+
<Badge color="success">ready</Badge>
|
|
258
|
+
</Group>
|
|
259
|
+
<TextInput label="Prompt" description="What to generate" />
|
|
260
|
+
<Alert color="info" title="Heads up">Costs Buzz.</Alert>
|
|
261
|
+
<Button fullWidth onClick={() => {/* … */}}>Generate</Button>
|
|
262
|
+
</Stack>
|
|
263
|
+
</Card>
|
|
264
|
+
</div>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**The components** (each with an exported props interface):
|
|
270
|
+
|
|
271
|
+
| Component | Highlights |
|
|
272
|
+
|---|---|
|
|
273
|
+
| `Button` | `variant` (`filled`/`light`/`outline`/`subtle`), `size`, `color`, `loading` (shows a `Loader`, disables + `aria-busy`), `fullWidth`, `leftSection`/`rightSection`. Defaults to `type="button"`. |
|
|
274
|
+
| `TextInput` / `Textarea` | `label` / `description` / `error` / `required`, wired via `htmlFor` + `aria-describedby` + `aria-invalid`. `Textarea` takes `minRows`. |
|
|
275
|
+
| `Card` | themed surface; `withBorder`, `padding`, `radius`. |
|
|
276
|
+
| `Stack` / `Group` | vertical / horizontal flex; `gap`, `align`, `justify` (+ `Group` `wrap`). |
|
|
277
|
+
| `Alert` | `color` (`info`/`success`/`warning`/`error`), `title`, `withCloseButton` + `onClose`; `role="alert"`. |
|
|
278
|
+
| `Loader` | CSS-keyframe spinner; `size`, `color`; `role="status"`. |
|
|
279
|
+
| `Badge` | `variant`, `size`, `color`. |
|
|
280
|
+
| `Modal` | `opened` + `onClose`, `title`, `size`; `role="dialog"` + `aria-modal`, Escape- and overlay-click-to-close, focuses the panel on open and restores focus on close. |
|
|
281
|
+
|
|
282
|
+
Each component forwards `className` + `style`, forwards a `ref` to its DOM
|
|
283
|
+
node (where it wraps one), and carries a `data-civitai-ui="<name>"` hook. Need
|
|
284
|
+
to inject the CSS yourself (SSR, or a non-React shell)? Call
|
|
285
|
+
`injectBlocksStyles(doc?)` once, or read the raw `BLOCKS_UI_STYLES` string.
|
|
286
|
+
`useBlocksStyles()` is the hook the components call internally.
|
|
287
|
+
|
|
288
|
+
> **Modal focus limitation (v0):** the modal focuses its panel on open and
|
|
289
|
+
> restores focus on close, but it does **not** trap focus — Tab can still reach
|
|
290
|
+
> content behind the overlay. That's fine for a simple confirm/settings dialog
|
|
291
|
+
> inside the sandboxed block; a full focus-trap is a v1 follow-up (kept
|
|
292
|
+
> dependency-free here on purpose).
|
|
293
|
+
|
|
294
|
+
### `SettingsForm`
|
|
295
|
+
|
|
296
|
+
The headless, manifest-driven settings form (its contract is intentionally
|
|
297
|
+
**unstyled native controls** — the host page themes it, so it does *not* use the
|
|
298
|
+
W6 pack):
|
|
212
299
|
|
|
213
300
|
```tsx
|
|
214
301
|
import { SettingsForm } from '@civitai/blocks-react/ui';
|
|
@@ -222,8 +309,8 @@ import { SettingsForm } from '@civitai/blocks-react/ui';
|
|
|
222
309
|
/>
|
|
223
310
|
```
|
|
224
311
|
|
|
225
|
-
|
|
226
|
-
|
|
312
|
+
`isFieldVisible` + `SettingsFormError` are also exported. See the `settings`
|
|
313
|
+
example.
|
|
227
314
|
|
|
228
315
|
## Lower-level transport
|
|
229
316
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type AlertColor = 'info' | 'success' | 'warning' | 'error';
|
|
2
|
+
export interface AlertProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'title'> {
|
|
3
|
+
/** Semantic color. Defaults to `'info'`. */
|
|
4
|
+
color?: AlertColor;
|
|
5
|
+
/** Optional bold title above the body. */
|
|
6
|
+
title?: React.ReactNode;
|
|
7
|
+
/** Render a close (×) button. Requires `onClose` to be meaningful. */
|
|
8
|
+
withCloseButton?: boolean;
|
|
9
|
+
/** Invoked when the close button is clicked. */
|
|
10
|
+
onClose?: () => void;
|
|
11
|
+
/** Accessible label for the close button. Defaults to "Close alert". */
|
|
12
|
+
closeButtonLabel?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A themed callout. Carries `role="alert"` so it's announced. Optional
|
|
16
|
+
* dismiss button calls `onClose`. Auto-themed via `useBlocksStyles()`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const Alert: import("react").ForwardRefExoticComponent<AlertProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
19
|
+
//# sourceMappingURL=Alert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Alert.d.ts","sourceRoot":"","sources":["../../src/ui/Alert.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAElE,MAAM,WAAW,UACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IACrE,4CAA4C;IAC5C,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,sEAAsE;IACtE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,uGAuChB,CAAC"}
|
package/dist/ui/Alert.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { useBlocksStyles } from './styles.js';
|
|
4
|
+
/**
|
|
5
|
+
* A themed callout. Carries `role="alert"` so it's announced. Optional
|
|
6
|
+
* dismiss button calls `onClose`. Auto-themed via `useBlocksStyles()`.
|
|
7
|
+
*/
|
|
8
|
+
export const Alert = forwardRef(function Alert({ color = 'info', title, withCloseButton = false, onClose, closeButtonLabel = 'Close alert', children, ...rest }, ref) {
|
|
9
|
+
useBlocksStyles();
|
|
10
|
+
return (_jsxs("div", { ref: ref, ...rest, "data-civitai-ui": "alert", "data-color": color, role: "alert", children: [_jsxs("div", { "data-civitai-ui-alert-body": true, children: [title != null ? (_jsx("div", { "data-civitai-ui-alert-title": true, children: title })) : null, children] }), withCloseButton ? (_jsx("button", { type: "button", "data-civitai-ui-alert-close": true, "aria-label": closeButtonLabel, onClick: () => onClose?.(), children: "\u00D7" })) : null] }));
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=Alert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Alert.js","sourceRoot":"","sources":["../../src/ui/Alert.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAkB9C;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA6B,SAAS,KAAK,CACxE,EACE,KAAK,GAAG,MAAM,EACd,KAAK,EACL,eAAe,GAAG,KAAK,EACvB,OAAO,EACP,gBAAgB,GAAG,aAAa,EAChC,QAAQ,EACR,GAAG,IAAI,EACR,EACD,GAAG;IAEH,eAAe,EAAE,CAAC;IAClB,OAAO,CACL,eACE,GAAG,EAAE,GAAG,KACJ,IAAI,qBACQ,OAAO,gBACX,KAAK,EACjB,IAAI,EAAC,OAAO,aAEZ,8DACG,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CACf,6DAAkC,KAAK,GAAO,CAC/C,CAAC,CAAC,CAAC,IAAI,EACP,QAAQ,IACL,EACL,eAAe,CAAC,CAAC,CAAC,CACjB,iBACE,IAAI,EAAC,QAAQ,qDAED,gBAAgB,EAC5B,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,uBAGnB,CACV,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type BadgeVariant = 'filled' | 'light' | 'outline';
|
|
2
|
+
export type BadgeSize = 'sm' | 'md' | 'lg';
|
|
3
|
+
export interface BadgeProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'color'> {
|
|
4
|
+
/** Visual style. Defaults to `'light'`. */
|
|
5
|
+
variant?: BadgeVariant;
|
|
6
|
+
/** Size preset. Defaults to `'md'`. */
|
|
7
|
+
size?: BadgeSize;
|
|
8
|
+
/**
|
|
9
|
+
* Accent color. `'primary'` (default), a semantic token name
|
|
10
|
+
* (`'error' | 'success' | 'warning' | 'info'`), or any CSS color string.
|
|
11
|
+
*/
|
|
12
|
+
color?: 'primary' | 'error' | 'success' | 'warning' | 'info' | (string & {});
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A small status/label pill. Wraps a `<span>` (ref-forwarded). Auto-themed.
|
|
16
|
+
*/
|
|
17
|
+
export declare const Badge: import("react").ForwardRefExoticComponent<BadgeProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
18
|
+
//# sourceMappingURL=Badge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../src/ui/Badge.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAC1D,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,MAAM,WAAW,UACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC5D,2CAA2C;IAC3C,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,uCAAuC;IACvC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;CAC9E;AAUD;;GAEG;AACH,eAAO,MAAM,KAAK,wGAyBhB,CAAC"}
|
package/dist/ui/Badge.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { useBlocksStyles } from './styles.js';
|
|
4
|
+
const SEMANTIC_COLORS = new Set(['error', 'success', 'warning', 'info']);
|
|
5
|
+
function resolveAccent(color) {
|
|
6
|
+
if (!color || color === 'primary')
|
|
7
|
+
return undefined;
|
|
8
|
+
if (SEMANTIC_COLORS.has(color))
|
|
9
|
+
return `var(--ci-color-${color})`;
|
|
10
|
+
return color;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A small status/label pill. Wraps a `<span>` (ref-forwarded). Auto-themed.
|
|
14
|
+
*/
|
|
15
|
+
export const Badge = forwardRef(function Badge({ variant = 'light', size = 'md', color = 'primary', style, children, ...rest }, ref) {
|
|
16
|
+
useBlocksStyles();
|
|
17
|
+
const accent = resolveAccent(color);
|
|
18
|
+
return (_jsx("span", { ref: ref, ...rest, "data-civitai-ui": "badge", "data-variant": variant, "data-size": size, style: accent
|
|
19
|
+
? {
|
|
20
|
+
['--ci-color-primary']: accent,
|
|
21
|
+
...style,
|
|
22
|
+
}
|
|
23
|
+
: style, children: children }));
|
|
24
|
+
});
|
|
25
|
+
//# sourceMappingURL=Badge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Badge.js","sourceRoot":"","sources":["../../src/ui/Badge.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAkB9C,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AAEzE,SAAS,aAAa,CAAC,KAA0B;IAC/C,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACpD,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,kBAAkB,KAAK,GAAG,CAAC;IAClE,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA8B,SAAS,KAAK,CACzE,EAAE,OAAO,GAAG,OAAO,EAAE,IAAI,GAAG,IAAI,EAAE,KAAK,GAAG,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAC/E,GAAG;IAEH,eAAe,EAAE,CAAC;IAClB,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,OAAO,CACL,eACE,GAAG,EAAE,GAAG,KACJ,IAAI,qBACQ,OAAO,kBACT,OAAO,eACV,IAAI,EACf,KAAK,EACH,MAAM;YACJ,CAAC,CAAE;gBACC,CAAC,oBAA8B,CAAC,EAAE,MAAM;gBACxC,GAAG,KAAK;aACe;YAC3B,CAAC,CAAC,KAAK,YAGV,QAAQ,GACJ,CACR,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type ButtonVariant = 'filled' | 'light' | 'outline' | 'subtle';
|
|
2
|
+
export type ButtonSize = 'sm' | 'md' | 'lg';
|
|
3
|
+
export interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'color'> {
|
|
4
|
+
/** Visual style. Defaults to `'filled'`. */
|
|
5
|
+
variant?: ButtonVariant;
|
|
6
|
+
/** Size preset. Defaults to `'md'`. */
|
|
7
|
+
size?: ButtonSize;
|
|
8
|
+
/**
|
|
9
|
+
* Accent color. `'primary'` (default) or a semantic token name
|
|
10
|
+
* (`'error' | 'success' | 'warning' | 'info'`), or any CSS color string —
|
|
11
|
+
* it overrides the `--ci-color-primary` the variant styling reads.
|
|
12
|
+
*/
|
|
13
|
+
color?: 'primary' | 'error' | 'success' | 'warning' | 'info' | (string & {});
|
|
14
|
+
/**
|
|
15
|
+
* Show a spinner and disable the button. Sets `aria-busy` and blocks clicks
|
|
16
|
+
* (both via the native `disabled` and a guard, so a controlled `onClick`
|
|
17
|
+
* never fires while loading).
|
|
18
|
+
*/
|
|
19
|
+
loading?: boolean;
|
|
20
|
+
/** Stretch to fill the container width. */
|
|
21
|
+
fullWidth?: boolean;
|
|
22
|
+
/** Content rendered before the label (icon, etc.). */
|
|
23
|
+
leftSection?: React.ReactNode;
|
|
24
|
+
/** Content rendered after the label. */
|
|
25
|
+
rightSection?: React.ReactNode;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Themed button. Wraps a native `<button>` (ref-forwarded, all native props
|
|
29
|
+
* pass through). `loading` shows a `<Loader>` and disables the control.
|
|
30
|
+
*
|
|
31
|
+
* Styling is automatic via `useBlocksStyles()`; the `data-variant` / `data-size`
|
|
32
|
+
* / `data-full-width` attributes drive the injected CSS.
|
|
33
|
+
*/
|
|
34
|
+
export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
35
|
+
//# sourceMappingURL=Button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../src/ui/Button.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AACtE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,MAAM,WAAW,WACf,SAAQ,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACpE,4CAA4C;IAC5C,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,uCAAuC;IACvC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAC7E;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,wCAAwC;IACxC,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAChC;AAUD;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,2GAmEjB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { Loader } from './Loader.js';
|
|
4
|
+
import { useBlocksStyles } from './styles.js';
|
|
5
|
+
const SEMANTIC_COLORS = new Set(['error', 'success', 'warning', 'info']);
|
|
6
|
+
function resolveAccent(color) {
|
|
7
|
+
if (!color || color === 'primary')
|
|
8
|
+
return undefined;
|
|
9
|
+
if (SEMANTIC_COLORS.has(color))
|
|
10
|
+
return `var(--ci-color-${color})`;
|
|
11
|
+
return color;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Themed button. Wraps a native `<button>` (ref-forwarded, all native props
|
|
15
|
+
* pass through). `loading` shows a `<Loader>` and disables the control.
|
|
16
|
+
*
|
|
17
|
+
* Styling is automatic via `useBlocksStyles()`; the `data-variant` / `data-size`
|
|
18
|
+
* / `data-full-width` attributes drive the injected CSS.
|
|
19
|
+
*/
|
|
20
|
+
export const Button = forwardRef(function Button({ variant = 'filled', size = 'md', color = 'primary', loading = false, fullWidth = false, leftSection, rightSection, disabled, onClick, children, style, type, ...rest }, ref) {
|
|
21
|
+
useBlocksStyles();
|
|
22
|
+
const accent = resolveAccent(color);
|
|
23
|
+
const isDisabled = disabled || loading;
|
|
24
|
+
return (_jsxs("button", { ref: ref,
|
|
25
|
+
// Default to type="button" — a block author dropping a <Button> into a
|
|
26
|
+
// <form> almost never wants an implicit submit. They can opt into
|
|
27
|
+
// type="submit" explicitly.
|
|
28
|
+
type: type ?? 'button', ...rest, "data-civitai-ui": "button", "data-variant": variant, "data-size": size, "data-full-width": fullWidth ? 'true' : undefined, disabled: isDisabled, "aria-busy": loading || undefined, onClick: (e) => {
|
|
29
|
+
// Belt-and-suspenders: a `disabled` <button> won't fire onClick, but
|
|
30
|
+
// guard anyway so `loading` reliably blocks the handler.
|
|
31
|
+
if (isDisabled) {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
onClick?.(e);
|
|
36
|
+
}, style: accent
|
|
37
|
+
? {
|
|
38
|
+
['--ci-color-primary']: accent,
|
|
39
|
+
['--ci-color-primary-hover']: accent,
|
|
40
|
+
...style,
|
|
41
|
+
}
|
|
42
|
+
: style, children: [loading ? _jsx(Loader, { size: "sm", "aria-hidden": "true" }) : null, leftSection != null ? (_jsx("span", { "data-civitai-ui-section": "left", children: leftSection })) : null, children, rightSection != null ? (_jsx("span", { "data-civitai-ui-section": "right", children: rightSection })) : null] }));
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=Button.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Button.js","sourceRoot":"","sources":["../../src/ui/Button.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AA+B9C,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AAEzE,SAAS,aAAa,CAAC,KAA2B;IAChD,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACpD,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,kBAAkB,KAAK,GAAG,CAAC;IAClE,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAiC,SAAS,MAAM,CAC9E,EACE,OAAO,GAAG,QAAQ,EAClB,IAAI,GAAG,IAAI,EACX,KAAK,GAAG,SAAS,EACjB,OAAO,GAAG,KAAK,EACf,SAAS,GAAG,KAAK,EACjB,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,GAAG,IAAI,EACR,EACD,GAAG;IAEH,eAAe,EAAE,CAAC;IAClB,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,QAAQ,IAAI,OAAO,CAAC;IACvC,OAAO,CACL,kBACE,GAAG,EAAE,GAAG;QACR,uEAAuE;QACvE,kEAAkE;QAClE,4BAA4B;QAC5B,IAAI,EAAE,IAAI,IAAI,QAAQ,KAIlB,IAAI,qBACQ,QAAQ,kBACV,OAAO,eACV,IAAI,qBACE,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAC/C,QAAQ,EAAE,UAAU,eACT,OAAO,IAAI,SAAS,EAC/B,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;YACb,qEAAqE;YACrE,yDAAyD;YACzD,IAAI,UAAU,EAAE,CAAC;gBACf,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,OAAO;YACT,CAAC;YACD,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QACf,CAAC,EACD,KAAK,EACH,MAAM;YACJ,CAAC,CAAE;gBACC,CAAC,oBAA8B,CAAC,EAAE,MAAM;gBACxC,CAAC,0BAAoC,CAAC,EAAE,MAAM;gBAC9C,GAAG,KAAK;aACe;YAC3B,CAAC,CAAC,KAAK,aAGV,OAAO,CAAC,CAAC,CAAC,KAAC,MAAM,IAAC,IAAI,EAAC,IAAI,iBAAa,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,EACxD,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,CACrB,0CAA8B,MAAM,YAAE,WAAW,GAAQ,CAC1D,CAAC,CAAC,CAAC,IAAI,EACP,QAAQ,EACR,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,CACtB,0CAA8B,OAAO,YAAE,YAAY,GAAQ,CAC5D,CAAC,CAAC,CAAC,IAAI,IACD,CACV,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type CardPadding = 'sm' | 'md' | 'lg';
|
|
2
|
+
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
/** Draw the themed border. Defaults to `true`. */
|
|
4
|
+
withBorder?: boolean;
|
|
5
|
+
/** Inner padding preset. Defaults to `'md'`. */
|
|
6
|
+
padding?: CardPadding;
|
|
7
|
+
/** Override the corner radius (any CSS length). Defaults to the token. */
|
|
8
|
+
radius?: string | number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A themed surface container. Wraps a `<div>` (ref-forwarded). Auto-themed.
|
|
12
|
+
*/
|
|
13
|
+
export declare const Card: import("react").ForwardRefExoticComponent<CardProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
14
|
+
//# sourceMappingURL=Card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../src/ui/Card.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE7C,MAAM,WAAW,SAAU,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACrE,kDAAkD;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gDAAgD;IAChD,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,eAAO,MAAM,IAAI,sGAiBf,CAAC"}
|
package/dist/ui/Card.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { useBlocksStyles } from './styles.js';
|
|
4
|
+
/**
|
|
5
|
+
* A themed surface container. Wraps a `<div>` (ref-forwarded). Auto-themed.
|
|
6
|
+
*/
|
|
7
|
+
export const Card = forwardRef(function Card({ withBorder = true, padding = 'md', radius, style, children, ...rest }, ref) {
|
|
8
|
+
useBlocksStyles();
|
|
9
|
+
return (_jsx("div", { ref: ref, ...rest, "data-civitai-ui": "card", "data-with-border": withBorder ? 'true' : undefined, "data-padding": padding, style: radius != null ? { borderRadius: radius, ...style } : style, children: children }));
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=Card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Card.js","sourceRoot":"","sources":["../../src/ui/Card.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAa9C;;GAEG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAA4B,SAAS,IAAI,CACrE,EAAE,UAAU,GAAG,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EACvE,GAAG;IAEH,eAAe,EAAE,CAAC;IAClB,OAAO,CACL,cACE,GAAG,EAAE,GAAG,KACJ,IAAI,qBACQ,MAAM,sBACJ,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,kBACnC,OAAO,EACrB,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,YAEjE,QAAQ,GACL,CACP,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface GroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
2
|
+
/** Gap between children (any CSS length, or a number → px). Defaults to 12. */
|
|
3
|
+
gap?: string | number;
|
|
4
|
+
/** `justify-content` value. */
|
|
5
|
+
justify?: React.CSSProperties['justifyContent'];
|
|
6
|
+
/** `align-items` value. Defaults to `'center'`. */
|
|
7
|
+
align?: React.CSSProperties['alignItems'];
|
|
8
|
+
/** Allow children to wrap onto multiple lines. Defaults to `true`. */
|
|
9
|
+
wrap?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Horizontal flex container. Wraps a `<div>` (ref-forwarded). Auto-themed.
|
|
13
|
+
*/
|
|
14
|
+
export declare const Group: import("react").ForwardRefExoticComponent<GroupProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
15
|
+
//# sourceMappingURL=Group.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Group.d.ts","sourceRoot":"","sources":["../../src/ui/Group.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACtE,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAChD,mDAAmD;IACnD,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IAC1C,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAOD;;GAEG;AACH,eAAO,MAAM,KAAK,uGAqBhB,CAAC"}
|
package/dist/ui/Group.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { useBlocksStyles } from './styles.js';
|
|
4
|
+
function toLength(v) {
|
|
5
|
+
if (v == null)
|
|
6
|
+
return undefined;
|
|
7
|
+
return typeof v === 'number' ? `${v}px` : v;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Horizontal flex container. Wraps a `<div>` (ref-forwarded). Auto-themed.
|
|
11
|
+
*/
|
|
12
|
+
export const Group = forwardRef(function Group({ gap = 12, justify, align = 'center', wrap = true, style, children, ...rest }, ref) {
|
|
13
|
+
useBlocksStyles();
|
|
14
|
+
return (_jsx("div", { ref: ref, ...rest, "data-civitai-ui": "group", style: {
|
|
15
|
+
gap: toLength(gap),
|
|
16
|
+
justifyContent: justify,
|
|
17
|
+
alignItems: align,
|
|
18
|
+
flexWrap: wrap ? 'wrap' : 'nowrap',
|
|
19
|
+
...style,
|
|
20
|
+
}, children: children }));
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=Group.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Group.js","sourceRoot":"","sources":["../../src/ui/Group.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAa9C,SAAS,QAAQ,CAAC,CAA8B;IAC9C,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IAChC,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA6B,SAAS,KAAK,CACxE,EAAE,GAAG,GAAG,EAAE,EAAE,OAAO,EAAE,KAAK,GAAG,QAAQ,EAAE,IAAI,GAAG,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAC9E,GAAG;IAEH,eAAe,EAAE,CAAC;IAClB,OAAO,CACL,cACE,GAAG,EAAE,GAAG,KACJ,IAAI,qBACQ,OAAO,EACvB,KAAK,EAAE;YACL,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC;YAClB,cAAc,EAAE,OAAO;YACvB,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;YAClC,GAAG,KAAK;SACT,YAEA,QAAQ,GACL,CACP,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type LoaderSize = 'sm' | 'md' | 'lg';
|
|
2
|
+
export interface LoaderProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'color'> {
|
|
3
|
+
/** Spinner diameter. Defaults to `'md'`. */
|
|
4
|
+
size?: LoaderSize;
|
|
5
|
+
/**
|
|
6
|
+
* Spinner color. Any CSS color; defaults to the primary token. Inside a
|
|
7
|
+
* `<Button loading>` the loader inherits the button's text color instead.
|
|
8
|
+
*/
|
|
9
|
+
color?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A CSS-keyframe spinner. Headless, auto-themed. Reused by `<Button loading>`.
|
|
13
|
+
*
|
|
14
|
+
* Carries `role="status"` + an accessible label so screen readers announce a
|
|
15
|
+
* busy state; pass `aria-label` to override the default "Loading".
|
|
16
|
+
*/
|
|
17
|
+
export declare const Loader: import("react").ForwardRefExoticComponent<LoaderProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
18
|
+
//# sourceMappingURL=Loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Loader.d.ts","sourceRoot":"","sources":["../../src/ui/Loader.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,MAAM,WAAW,WACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC5D,4CAA4C;IAC5C,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,eAAO,MAAM,MAAM,yGAkBjB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { useBlocksStyles } from './styles.js';
|
|
4
|
+
/**
|
|
5
|
+
* A CSS-keyframe spinner. Headless, auto-themed. Reused by `<Button loading>`.
|
|
6
|
+
*
|
|
7
|
+
* Carries `role="status"` + an accessible label so screen readers announce a
|
|
8
|
+
* busy state; pass `aria-label` to override the default "Loading".
|
|
9
|
+
*/
|
|
10
|
+
export const Loader = forwardRef(function Loader({ size = 'md', color, style, ...rest }, ref) {
|
|
11
|
+
useBlocksStyles();
|
|
12
|
+
return (_jsx("span", { ref: ref, ...rest, "data-civitai-ui": "loader", "data-size": size, role: "status", "aria-label": rest['aria-label'] ?? 'Loading', style: color ? { color, ...style } : style }));
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=Loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Loader.js","sourceRoot":"","sources":["../../src/ui/Loader.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAe9C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAA+B,SAAS,MAAM,CAC5E,EAAE,IAAI,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EACtC,GAAG;IAEH,eAAe,EAAE,CAAC;IAClB,OAAO,CACL,eACE,GAAG,EAAE,GAAG,KACJ,IAAI,qBACQ,QAAQ,eACb,IAAI,EACf,IAAI,EAAC,QAAQ,gBAGD,IAAI,CAAC,YAAY,CAAC,IAAI,SAAS,EAC3C,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,GAC1C,CACH,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type ModalSize = 'sm' | 'md' | 'lg';
|
|
2
|
+
export interface ModalProps {
|
|
3
|
+
/** Whether the modal is shown. When `false`, nothing is rendered. */
|
|
4
|
+
opened: boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Called when the user requests close — Escape, overlay click, or the
|
|
7
|
+
* header close button. The parent owns `opened`, so it must flip it.
|
|
8
|
+
*/
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
/** Optional header title (also wires `aria-labelledby`). */
|
|
11
|
+
title?: React.ReactNode;
|
|
12
|
+
/** Panel width preset. Defaults to `'md'`. */
|
|
13
|
+
size?: ModalSize;
|
|
14
|
+
/** Show the header close (×) button. Defaults to `true`. */
|
|
15
|
+
withCloseButton?: boolean;
|
|
16
|
+
/** Close when the dimmed overlay (outside the panel) is clicked. Default `true`. */
|
|
17
|
+
closeOnOverlayClick?: boolean;
|
|
18
|
+
/** Close when Escape is pressed. Default `true`. */
|
|
19
|
+
closeOnEscape?: boolean;
|
|
20
|
+
/** Accessible label for the close button. Defaults to "Close". */
|
|
21
|
+
closeButtonLabel?: string;
|
|
22
|
+
/** Class applied to the panel element. */
|
|
23
|
+
className?: string;
|
|
24
|
+
/** Style applied to the panel element. */
|
|
25
|
+
style?: React.CSSProperties;
|
|
26
|
+
/** Modal body content. */
|
|
27
|
+
children?: React.ReactNode;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A lightweight modal dialog.
|
|
31
|
+
*
|
|
32
|
+
* `position: fixed` overlay covers the iframe viewport — correct here, since
|
|
33
|
+
* the iframe IS the block's surface (no host bleed-through to worry about).
|
|
34
|
+
*
|
|
35
|
+
* Accessibility: `role="dialog"` + `aria-modal="true"`, `aria-labelledby` when
|
|
36
|
+
* a `title` is given. On open the panel is focused and the previously-focused
|
|
37
|
+
* element is restored on close. Escape and overlay-click both call `onClose`;
|
|
38
|
+
* a click inside the panel does NOT.
|
|
39
|
+
*
|
|
40
|
+
* v0 limitation (documented): this does NOT trap focus inside the panel — Tab
|
|
41
|
+
* can still reach content behind the overlay. Sufficient for a simple
|
|
42
|
+
* confirm/settings dialog inside the sandboxed block; a full focus-trap is a
|
|
43
|
+
* v1 follow-up (kept dependency-free here on purpose).
|
|
44
|
+
*/
|
|
45
|
+
export declare function Modal(props: ModalProps): React.JSX.Element | null;
|
|
46
|
+
//# sourceMappingURL=Modal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../src/ui/Modal.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,4DAA4D;IAC5D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oFAAoF;IACpF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,oDAAoD;IACpD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAmGjE"}
|
package/dist/ui/Modal.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useId, useRef } from 'react';
|
|
3
|
+
import { useBlocksStyles } from './styles.js';
|
|
4
|
+
/**
|
|
5
|
+
* A lightweight modal dialog.
|
|
6
|
+
*
|
|
7
|
+
* `position: fixed` overlay covers the iframe viewport — correct here, since
|
|
8
|
+
* the iframe IS the block's surface (no host bleed-through to worry about).
|
|
9
|
+
*
|
|
10
|
+
* Accessibility: `role="dialog"` + `aria-modal="true"`, `aria-labelledby` when
|
|
11
|
+
* a `title` is given. On open the panel is focused and the previously-focused
|
|
12
|
+
* element is restored on close. Escape and overlay-click both call `onClose`;
|
|
13
|
+
* a click inside the panel does NOT.
|
|
14
|
+
*
|
|
15
|
+
* v0 limitation (documented): this does NOT trap focus inside the panel — Tab
|
|
16
|
+
* can still reach content behind the overlay. Sufficient for a simple
|
|
17
|
+
* confirm/settings dialog inside the sandboxed block; a full focus-trap is a
|
|
18
|
+
* v1 follow-up (kept dependency-free here on purpose).
|
|
19
|
+
*/
|
|
20
|
+
export function Modal(props) {
|
|
21
|
+
const { opened, onClose, title, size = 'md', withCloseButton = true, closeOnOverlayClick = true, closeOnEscape = true, closeButtonLabel = 'Close', className, style, children, } = props;
|
|
22
|
+
useBlocksStyles();
|
|
23
|
+
const panelRef = useRef(null);
|
|
24
|
+
const previouslyFocused = useRef(null);
|
|
25
|
+
const reactId = useId();
|
|
26
|
+
const titleId = `ci-modal-title-${reactId}`;
|
|
27
|
+
// Focus the panel on open; restore focus to the prior element on close.
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!opened)
|
|
30
|
+
return;
|
|
31
|
+
previouslyFocused.current =
|
|
32
|
+
typeof document !== 'undefined' ? document.activeElement : null;
|
|
33
|
+
// Focus after paint so the element exists and is focusable.
|
|
34
|
+
panelRef.current?.focus();
|
|
35
|
+
return () => {
|
|
36
|
+
const prev = previouslyFocused.current;
|
|
37
|
+
if (prev instanceof HTMLElement)
|
|
38
|
+
prev.focus();
|
|
39
|
+
};
|
|
40
|
+
}, [opened]);
|
|
41
|
+
// Escape-to-close. Listener only attached while open.
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!opened || !closeOnEscape)
|
|
44
|
+
return;
|
|
45
|
+
const onKeyDown = (e) => {
|
|
46
|
+
if (e.key === 'Escape') {
|
|
47
|
+
// Don't stopPropagation — that swallows Escape unpredictably when two
|
|
48
|
+
// modals (or an author's own document Escape handler) are present. v0
|
|
49
|
+
// assumes a single modal, so letting the event continue is correct.
|
|
50
|
+
onClose();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
document.addEventListener('keydown', onKeyDown);
|
|
54
|
+
return () => document.removeEventListener('keydown', onKeyDown);
|
|
55
|
+
}, [opened, closeOnEscape, onClose]);
|
|
56
|
+
if (!opened)
|
|
57
|
+
return null;
|
|
58
|
+
return (_jsx("div", { "data-civitai-ui": "modal-overlay", onMouseDown: (e) => {
|
|
59
|
+
// Only close on a click that starts AND lands on the overlay itself —
|
|
60
|
+
// a drag that began inside the panel and released on the overlay must
|
|
61
|
+
// not close it.
|
|
62
|
+
if (closeOnOverlayClick && e.target === e.currentTarget) {
|
|
63
|
+
onClose();
|
|
64
|
+
}
|
|
65
|
+
}, children: _jsxs("div", { ref: panelRef, className: className, "data-civitai-ui": "modal", "data-size": size, role: "dialog", "aria-modal": "true", "aria-labelledby": title != null ? titleId : undefined, tabIndex: -1, style: style, children: [title != null || withCloseButton ? (_jsxs("div", { "data-civitai-ui-modal-header": true, children: [title != null ? (_jsx("h2", { id: titleId, "data-civitai-ui-modal-title": true, children: title })) : (_jsx("span", {})), withCloseButton ? (_jsx("button", { type: "button", "data-civitai-ui-modal-close": true, "aria-label": closeButtonLabel, onClick: () => onClose(), children: "\u00D7" })) : null] })) : null, _jsx("div", { "data-civitai-ui-modal-body": true, children: children })] }) }));
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=Modal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../src/ui/Modal.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface StackProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
2
|
+
/** Gap between children (any CSS length, or a number → px). Defaults to 12. */
|
|
3
|
+
gap?: string | number;
|
|
4
|
+
/** `align-items` value. */
|
|
5
|
+
align?: React.CSSProperties['alignItems'];
|
|
6
|
+
/** `justify-content` value. */
|
|
7
|
+
justify?: React.CSSProperties['justifyContent'];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Vertical flex container. Wraps a `<div>` (ref-forwarded). Auto-themed.
|
|
11
|
+
*/
|
|
12
|
+
export declare const Stack: import("react").ForwardRefExoticComponent<StackProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
13
|
+
//# sourceMappingURL=Stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Stack.d.ts","sourceRoot":"","sources":["../../src/ui/Stack.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACtE,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IAC1C,+BAA+B;IAC/B,OAAO,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;CACjD;AAOD;;GAEG;AACH,eAAO,MAAM,KAAK,uGAoBhB,CAAC"}
|
package/dist/ui/Stack.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { useBlocksStyles } from './styles.js';
|
|
4
|
+
function toLength(v) {
|
|
5
|
+
if (v == null)
|
|
6
|
+
return undefined;
|
|
7
|
+
return typeof v === 'number' ? `${v}px` : v;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Vertical flex container. Wraps a `<div>` (ref-forwarded). Auto-themed.
|
|
11
|
+
*/
|
|
12
|
+
export const Stack = forwardRef(function Stack({ gap = 12, align, justify, style, children, ...rest }, ref) {
|
|
13
|
+
useBlocksStyles();
|
|
14
|
+
return (_jsx("div", { ref: ref, ...rest, "data-civitai-ui": "stack", style: {
|
|
15
|
+
gap: toLength(gap),
|
|
16
|
+
alignItems: align,
|
|
17
|
+
justifyContent: justify,
|
|
18
|
+
...style,
|
|
19
|
+
}, children: children }));
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=Stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Stack.js","sourceRoot":"","sources":["../../src/ui/Stack.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAW9C,SAAS,QAAQ,CAAC,CAA8B;IAC9C,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IAChC,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA6B,SAAS,KAAK,CACxE,EAAE,GAAG,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EACtD,GAAG;IAEH,eAAe,EAAE,CAAC;IAClB,OAAO,CACL,cACE,GAAG,EAAE,GAAG,KACJ,IAAI,qBACQ,OAAO,EACvB,KAAK,EAAE;YACL,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC;YAClB,UAAU,EAAE,KAAK;YACjB,cAAc,EAAE,OAAO;YACvB,GAAG,KAAK;SACT,YAEA,QAAQ,GACL,CACP,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface TextInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
2
|
+
/** Visible field label, rendered in a `<label>` linked to the input. */
|
|
3
|
+
label?: React.ReactNode;
|
|
4
|
+
/** Helper text under the label, linked via `aria-describedby`. */
|
|
5
|
+
description?: React.ReactNode;
|
|
6
|
+
/**
|
|
7
|
+
* Error message. When set, the input gets `aria-invalid="true"`, the message
|
|
8
|
+
* is announced (`role="alert"`) and linked via `aria-describedby`.
|
|
9
|
+
*/
|
|
10
|
+
error?: React.ReactNode;
|
|
11
|
+
/** Mark required: shows an asterisk and sets the native `required`. */
|
|
12
|
+
required?: boolean;
|
|
13
|
+
/** Class on the wrapping element (the `<input>` is `inputClassName`). */
|
|
14
|
+
className?: string;
|
|
15
|
+
/** Class applied to the native `<input>`. */
|
|
16
|
+
inputClassName?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Labeled text input. Wraps a native `<input>` (ref-forwarded). The label,
|
|
20
|
+
* description and error are wired to the control via `htmlFor` / `id` /
|
|
21
|
+
* `aria-describedby` / `aria-invalid` so the field is announced correctly.
|
|
22
|
+
*
|
|
23
|
+
* Auto-themed via `useBlocksStyles()`.
|
|
24
|
+
*/
|
|
25
|
+
export declare const TextInput: import("react").ForwardRefExoticComponent<TextInputProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
26
|
+
//# sourceMappingURL=TextInput.d.ts.map
|