@birdycom/omniaui-react 0.11.0 → 0.11.1
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/LICENSE +21 -21
- package/README.md +21 -21
- package/package.json +71 -71
- package/src/blocks/BlockAboutTimeline.tsx +46 -46
- package/src/blocks/BlockAppHeader.tsx +52 -52
- package/src/blocks/BlockAppShell.tsx +56 -56
- package/src/blocks/BlockAuth.tsx +53 -53
- package/src/blocks/BlockAuthSplit.tsx +84 -84
- package/src/blocks/BlockContactGrid.tsx +71 -71
- package/src/blocks/BlockCta.tsx +23 -23
- package/src/blocks/BlockDataTable.tsx +57 -57
- package/src/blocks/BlockFaq.tsx +55 -55
- package/src/blocks/BlockFeatures.tsx +38 -38
- package/src/blocks/BlockFooter.tsx +50 -50
- package/src/blocks/BlockFormPage.tsx +26 -26
- package/src/blocks/BlockHero.tsx +27 -27
- package/src/blocks/BlockHeroCode.tsx +63 -63
- package/src/blocks/BlockHeroOrbit.tsx +73 -73
- package/src/blocks/BlockHeroSplit.tsx +28 -28
- package/src/blocks/BlockModuleHeader.tsx +34 -34
- package/src/blocks/BlockPageHero.tsx +25 -25
- package/src/blocks/BlockPortfolioGrid.tsx +50 -50
- package/src/blocks/BlockPricing.tsx +62 -62
- package/src/blocks/BlockProcess.tsx +50 -50
- package/src/blocks/BlockProductGrid.tsx +54 -54
- package/src/blocks/BlockProfileHeader.tsx +57 -57
- package/src/blocks/BlockShowcase.tsx +58 -58
- package/src/blocks/BlockSiteFooter.tsx +60 -60
- package/src/blocks/BlockSiteHeader.tsx +42 -42
- package/src/blocks/BlockSpotlight.tsx +58 -58
- package/src/blocks/BlockStats.tsx +26 -26
- package/src/blocks/BlockStatsCounter.tsx +31 -31
- package/src/blocks/BlockTeamList.tsx +48 -48
- package/src/blocks/BlockTwoFactor.tsx +57 -57
- package/src/blocks/BlockWizardDialog.tsx +101 -101
- package/src/components/Accordion.tsx +31 -31
- package/src/components/AccordionAccent.tsx +39 -39
- package/src/components/AffixInput.tsx +47 -47
- package/src/components/Alert.tsx +24 -24
- package/src/components/AlertDialog.tsx +60 -60
- package/src/components/AlphaTabs.tsx +63 -63
- package/src/components/Attachment.tsx +140 -140
- package/src/components/Avatar.tsx +32 -32
- package/src/components/BackLink.tsx +11 -11
- package/src/components/Badge.tsx +14 -14
- package/src/components/BarcodeInput.tsx +64 -64
- package/src/components/Breadcrumb.tsx +32 -32
- package/src/components/BrowserFrame.tsx +48 -48
- package/src/components/BusyOverlay.tsx +52 -52
- package/src/components/Button.tsx +49 -49
- package/src/components/Card.tsx +27 -27
- package/src/components/ChartLine.tsx +119 -119
- package/src/components/ChatComposer.tsx +73 -73
- package/src/components/Checkbox.tsx +14 -14
- package/src/components/Checklist.tsx +45 -45
- package/src/components/ChipFilter.tsx +60 -60
- package/src/components/Collapsible.tsx +43 -43
- package/src/components/ColorField.tsx +48 -48
- package/src/components/ColorSelect.tsx +49 -49
- package/src/components/ColumnMenu.tsx +66 -66
- package/src/components/Command.tsx +113 -113
- package/src/components/ConfirmDialog.tsx +95 -95
- package/src/components/CookieConsent.tsx +137 -137
- package/src/components/CopyField.tsx +44 -44
- package/src/components/Counter.tsx +21 -21
- package/src/components/DataTable.tsx +201 -201
- package/src/components/DecimalInput.tsx +87 -87
- package/src/components/DefinitionList.tsx +38 -38
- package/src/components/DocViewer.tsx +110 -110
- package/src/components/Dropdown.tsx +52 -52
- package/src/components/Dropzone.tsx +46 -46
- package/src/components/EmojiPicker.tsx +72 -72
- package/src/components/EmptyState.tsx +27 -27
- package/src/components/EntityCard.tsx +56 -56
- package/src/components/FactList.tsx +43 -43
- package/src/components/FilterCounts.tsx +50 -50
- package/src/components/FlipCard.tsx +55 -55
- package/src/components/FormField.tsx +54 -54
- package/src/components/Greeting.tsx +63 -63
- package/src/components/Heading.tsx +27 -27
- package/src/components/Icon.tsx +25 -25
- package/src/components/ImageZoom.tsx +51 -51
- package/src/components/Input.tsx +21 -21
- package/src/components/InputGroup.tsx +24 -24
- package/src/components/LanguageSelect.tsx +64 -64
- package/src/components/LauncherCard.tsx +50 -50
- package/src/components/Lightbox.tsx +83 -83
- package/src/components/LinkArrow.tsx +27 -27
- package/src/components/ListToolbar.tsx +67 -67
- package/src/components/Marquee.tsx +38 -38
- package/src/components/MediaCard.tsx +47 -47
- package/src/components/MediaRow.tsx +53 -53
- package/src/components/MediaThumb.tsx +49 -49
- package/src/components/Message.tsx +171 -171
- package/src/components/MessageThread.tsx +177 -177
- package/src/components/Modal.tsx +81 -81
- package/src/components/Navmenu.tsx +62 -62
- package/src/components/NextTeaser.tsx +29 -29
- package/src/components/NumberInput.tsx +64 -64
- package/src/components/OtpInput.tsx +48 -48
- package/src/components/PageHeader.tsx +43 -43
- package/src/components/Pagination.tsx +42 -42
- package/src/components/PasswordInput.tsx +36 -36
- package/src/components/PasswordRequirements.tsx +46 -46
- package/src/components/PhoneInput.tsx +93 -93
- package/src/components/Popover.tsx +34 -34
- package/src/components/PriceCard.tsx +83 -83
- package/src/components/ProductMark.tsx +35 -35
- package/src/components/Progress.tsx +32 -32
- package/src/components/ProjectCard.tsx +46 -46
- package/src/components/Prose.tsx +79 -79
- package/src/components/RecoveryCodes.tsx +65 -65
- package/src/components/Segmented.tsx +47 -47
- package/src/components/Select.tsx +27 -27
- package/src/components/SeverityCard.tsx +53 -53
- package/src/components/Sheet.tsx +62 -62
- package/src/components/Skeleton.tsx +12 -12
- package/src/components/SortableRow.tsx +46 -46
- package/src/components/StackCard.tsx +40 -40
- package/src/components/StackList.tsx +42 -42
- package/src/components/StatCard.tsx +49 -49
- package/src/components/Stepper.tsx +54 -54
- package/src/components/Switch.tsx +18 -18
- package/src/components/Table.tsx +75 -75
- package/src/components/TableActions.tsx +54 -54
- package/src/components/TableCompact.tsx +61 -61
- package/src/components/Tabs.tsx +45 -45
- package/src/components/TagInput.tsx +73 -73
- package/src/components/TerminalLog.tsx +63 -63
- package/src/components/Textarea.tsx +19 -19
- package/src/components/ThemeSwitch.tsx +45 -45
- package/src/components/TimeInput.tsx +76 -76
- package/src/components/Timeline.tsx +45 -45
- package/src/components/Toast.tsx +138 -138
- package/src/components/ToggleField.tsx +39 -39
- package/src/components/Tooltip.tsx +14 -14
- package/src/components/TopProgress.tsx +54 -54
- package/src/components/Tree.tsx +97 -97
- package/src/components/UpgradeBanner.tsx +62 -62
- package/src/components/User.tsx +31 -31
- package/src/components/VersionBanner.tsx +41 -41
- package/src/hooks.tsx +141 -141
- package/src/utils.ts +25 -25
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
// File: packages/react/src/components/BarcodeInput.tsx
|
|
2
|
-
// Map: props -> component (label, digits-only field, preview figure with placeholder bars or your own graphic)
|
|
3
|
-
// Same markup as packages/ui/snippets/barcode-input.html. No barcode drawing: pass your own <svg>/<canvas> as children.
|
|
4
|
-
"use client";
|
|
5
|
-
|
|
6
|
-
import { forwardRef, useId } from "react";
|
|
7
|
-
import type { InputHTMLAttributes, ReactNode } from "react";
|
|
8
|
-
import { cx } from "../utils";
|
|
9
|
-
import { useControllable } from "../hooks";
|
|
10
|
-
|
|
11
|
-
export interface BarcodeInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "children"> {
|
|
12
|
-
value?: string;
|
|
13
|
-
defaultValue?: string;
|
|
14
|
-
onChange?: (value: string) => void;
|
|
15
|
-
/** Visible label above the field. */
|
|
16
|
-
label?: ReactNode;
|
|
17
|
-
/** Text in the preview while the field is empty. */
|
|
18
|
-
emptyText?: string;
|
|
19
|
-
error?: boolean;
|
|
20
|
-
/** Your own barcode graphic; replaces the placeholder bars. */
|
|
21
|
-
children?: ReactNode;
|
|
22
|
-
/** Class for the wrapper (`className` goes to the input). */
|
|
23
|
-
wrapperClassName?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** `.oc-barcode-input`: code field + preview. Keeps digits only (hand scanners type like a keyboard). */
|
|
27
|
-
export const BarcodeInput = forwardRef<HTMLInputElement, BarcodeInputProps>(function BarcodeInput(
|
|
28
|
-
{ value, defaultValue, onChange, label, emptyText = "No code yet", error, children, wrapperClassName, className, id, ...rest },
|
|
29
|
-
ref,
|
|
30
|
-
) {
|
|
31
|
-
const auto = `oc-barcode-${useId().replace(/:/g, "")}`;
|
|
32
|
-
const inputId = id ?? auto;
|
|
33
|
-
const [code, setCode] = useControllable<string>(value, defaultValue ?? "", onChange);
|
|
34
|
-
const empty = !code;
|
|
35
|
-
return (
|
|
36
|
-
<div className={cx("oc-barcode-input", wrapperClassName)}>
|
|
37
|
-
{label != null && <label className="oc-field-label" htmlFor={inputId}>{label}</label>}
|
|
38
|
-
<div className="oc-input-group oc-input-group--affix">
|
|
39
|
-
<span className="oc-input-addon" aria-hidden="true">
|
|
40
|
-
<svg viewBox="0 0 16 16"><path fill="currentColor" d="M1 1h1.4v14H1zm2.2 0h.7v14h-.7zm1.6 0h1.5v14H4.8zm2.4 0h.7v14H7.2zm1.6 0h.7v14H8.8zm1.5 0h1.4v14h-1.4zm2.2 0H15v14h-.9z" /></svg>
|
|
41
|
-
</span>
|
|
42
|
-
<input
|
|
43
|
-
ref={ref}
|
|
44
|
-
id={inputId}
|
|
45
|
-
className={cx("oc-input", "oc-barcode-input-field", className)}
|
|
46
|
-
type="text"
|
|
47
|
-
inputMode="text"
|
|
48
|
-
autoComplete="off"
|
|
49
|
-
spellCheck={false}
|
|
50
|
-
maxLength={80}
|
|
51
|
-
placeholder="EAN-13 or internal code"
|
|
52
|
-
aria-invalid={error ? true : undefined}
|
|
53
|
-
value={code}
|
|
54
|
-
onChange={(e) => setCode(e.target.value)}
|
|
55
|
-
{...rest}
|
|
56
|
-
/>
|
|
57
|
-
</div>
|
|
58
|
-
<figure className={cx("oc-barcode-preview", empty && "oc-barcode-preview--empty")} aria-label="Barcode preview">
|
|
59
|
-
{children ?? <div className="oc-barcode-preview-bars" aria-hidden="true" />}
|
|
60
|
-
<figcaption className="oc-barcode-preview-code">{empty ? emptyText : code}</figcaption>
|
|
61
|
-
</figure>
|
|
62
|
-
</div>
|
|
63
|
-
);
|
|
64
|
-
});
|
|
1
|
+
// File: packages/react/src/components/BarcodeInput.tsx
|
|
2
|
+
// Map: props -> component (label, digits-only field, preview figure with placeholder bars or your own graphic)
|
|
3
|
+
// Same markup as packages/ui/snippets/barcode-input.html. No barcode drawing: pass your own <svg>/<canvas> as children.
|
|
4
|
+
"use client";
|
|
5
|
+
|
|
6
|
+
import { forwardRef, useId } from "react";
|
|
7
|
+
import type { InputHTMLAttributes, ReactNode } from "react";
|
|
8
|
+
import { cx } from "../utils";
|
|
9
|
+
import { useControllable } from "../hooks";
|
|
10
|
+
|
|
11
|
+
export interface BarcodeInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "children"> {
|
|
12
|
+
value?: string;
|
|
13
|
+
defaultValue?: string;
|
|
14
|
+
onChange?: (value: string) => void;
|
|
15
|
+
/** Visible label above the field. */
|
|
16
|
+
label?: ReactNode;
|
|
17
|
+
/** Text in the preview while the field is empty. */
|
|
18
|
+
emptyText?: string;
|
|
19
|
+
error?: boolean;
|
|
20
|
+
/** Your own barcode graphic; replaces the placeholder bars. */
|
|
21
|
+
children?: ReactNode;
|
|
22
|
+
/** Class for the wrapper (`className` goes to the input). */
|
|
23
|
+
wrapperClassName?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** `.oc-barcode-input`: code field + preview. Keeps digits only (hand scanners type like a keyboard). */
|
|
27
|
+
export const BarcodeInput = forwardRef<HTMLInputElement, BarcodeInputProps>(function BarcodeInput(
|
|
28
|
+
{ value, defaultValue, onChange, label, emptyText = "No code yet", error, children, wrapperClassName, className, id, ...rest },
|
|
29
|
+
ref,
|
|
30
|
+
) {
|
|
31
|
+
const auto = `oc-barcode-${useId().replace(/:/g, "")}`;
|
|
32
|
+
const inputId = id ?? auto;
|
|
33
|
+
const [code, setCode] = useControllable<string>(value, defaultValue ?? "", onChange);
|
|
34
|
+
const empty = !code;
|
|
35
|
+
return (
|
|
36
|
+
<div className={cx("oc-barcode-input", wrapperClassName)}>
|
|
37
|
+
{label != null && <label className="oc-field-label" htmlFor={inputId}>{label}</label>}
|
|
38
|
+
<div className="oc-input-group oc-input-group--affix">
|
|
39
|
+
<span className="oc-input-addon" aria-hidden="true">
|
|
40
|
+
<svg viewBox="0 0 16 16"><path fill="currentColor" d="M1 1h1.4v14H1zm2.2 0h.7v14h-.7zm1.6 0h1.5v14H4.8zm2.4 0h.7v14H7.2zm1.6 0h.7v14H8.8zm1.5 0h1.4v14h-1.4zm2.2 0H15v14h-.9z" /></svg>
|
|
41
|
+
</span>
|
|
42
|
+
<input
|
|
43
|
+
ref={ref}
|
|
44
|
+
id={inputId}
|
|
45
|
+
className={cx("oc-input", "oc-barcode-input-field", className)}
|
|
46
|
+
type="text"
|
|
47
|
+
inputMode="text"
|
|
48
|
+
autoComplete="off"
|
|
49
|
+
spellCheck={false}
|
|
50
|
+
maxLength={80}
|
|
51
|
+
placeholder="EAN-13 or internal code"
|
|
52
|
+
aria-invalid={error ? true : undefined}
|
|
53
|
+
value={code}
|
|
54
|
+
onChange={(e) => setCode(e.target.value)}
|
|
55
|
+
{...rest}
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
<figure className={cx("oc-barcode-preview", empty && "oc-barcode-preview--empty")} aria-label="Barcode preview">
|
|
59
|
+
{children ?? <div className="oc-barcode-preview-bars" aria-hidden="true" />}
|
|
60
|
+
<figcaption className="oc-barcode-preview-code">{empty ? emptyText : code}</figcaption>
|
|
61
|
+
</figure>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
});
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { forwardRef } from "react";
|
|
2
|
-
import type { HTMLAttributes, ReactNode } from "react";
|
|
3
|
-
import { cx } from "../utils";
|
|
4
|
-
|
|
5
|
-
export interface BreadcrumbItemData {
|
|
6
|
-
label: ReactNode;
|
|
7
|
-
/** Omit (or put last) to mark the current page. */
|
|
8
|
-
href?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface BreadcrumbProps extends HTMLAttributes<HTMLElement> {
|
|
12
|
-
items?: BreadcrumbItemData[];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** `<nav class="oc-breadcrumb">` with links and a final `aria-current="page"` item. */
|
|
16
|
-
export const Breadcrumb = forwardRef<HTMLElement, BreadcrumbProps>(function Breadcrumb(
|
|
17
|
-
{ items, children, className, "aria-label": ariaLabel = "Breadcrumb", ...rest },
|
|
18
|
-
ref,
|
|
19
|
-
) {
|
|
20
|
-
return (
|
|
21
|
-
<nav ref={ref} className={cx("oc-breadcrumb", className)} aria-label={ariaLabel} {...rest}>
|
|
22
|
-
{items?.map((item, i) =>
|
|
23
|
-
item.href && i < items.length - 1 ? (
|
|
24
|
-
<a key={i} className="oc-breadcrumb-item" href={item.href}>{item.label}</a>
|
|
25
|
-
) : (
|
|
26
|
-
<span key={i} className="oc-breadcrumb-item" aria-current="page">{item.label}</span>
|
|
27
|
-
),
|
|
28
|
-
)}
|
|
29
|
-
{children}
|
|
30
|
-
</nav>
|
|
31
|
-
);
|
|
32
|
-
});
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
3
|
+
import { cx } from "../utils";
|
|
4
|
+
|
|
5
|
+
export interface BreadcrumbItemData {
|
|
6
|
+
label: ReactNode;
|
|
7
|
+
/** Omit (or put last) to mark the current page. */
|
|
8
|
+
href?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BreadcrumbProps extends HTMLAttributes<HTMLElement> {
|
|
12
|
+
items?: BreadcrumbItemData[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** `<nav class="oc-breadcrumb">` with links and a final `aria-current="page"` item. */
|
|
16
|
+
export const Breadcrumb = forwardRef<HTMLElement, BreadcrumbProps>(function Breadcrumb(
|
|
17
|
+
{ items, children, className, "aria-label": ariaLabel = "Breadcrumb", ...rest },
|
|
18
|
+
ref,
|
|
19
|
+
) {
|
|
20
|
+
return (
|
|
21
|
+
<nav ref={ref} className={cx("oc-breadcrumb", className)} aria-label={ariaLabel} {...rest}>
|
|
22
|
+
{items?.map((item, i) =>
|
|
23
|
+
item.href && i < items.length - 1 ? (
|
|
24
|
+
<a key={i} className="oc-breadcrumb-item" href={item.href}>{item.label}</a>
|
|
25
|
+
) : (
|
|
26
|
+
<span key={i} className="oc-breadcrumb-item" aria-current="page">{item.label}</span>
|
|
27
|
+
),
|
|
28
|
+
)}
|
|
29
|
+
{children}
|
|
30
|
+
</nav>
|
|
31
|
+
);
|
|
32
|
+
});
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
// File: packages/react/src/components/BrowserFrame.tsx
|
|
2
|
-
// Map: props -> BrowserFrame (figure -> bar: dots, address, actions -> view (img or children) -> caption)
|
|
3
|
-
// Same markup as packages/ui/snippets/browser-frame.html.
|
|
4
|
-
import { forwardRef } from "react";
|
|
5
|
-
import type { CSSProperties, HTMLAttributes, ReactNode } from "react";
|
|
6
|
-
import { cx } from "../utils";
|
|
7
|
-
|
|
8
|
-
export interface BrowserFrameProps extends HTMLAttributes<HTMLElement> {
|
|
9
|
-
/** Text in the address pill. Leave out (or use `minimal`) for dots only. */
|
|
10
|
-
url?: string;
|
|
11
|
-
/** Screenshot URL. `children` replace it (live content). */
|
|
12
|
-
src?: string;
|
|
13
|
-
alt?: string;
|
|
14
|
-
caption?: ReactNode;
|
|
15
|
-
/** Small glyphs on the right of the bar. */
|
|
16
|
-
actions?: ReactNode;
|
|
17
|
-
minimal?: boolean;
|
|
18
|
-
dark?: boolean;
|
|
19
|
-
tilt?: boolean;
|
|
20
|
-
/** View height follows the content instead of the ratio. */
|
|
21
|
-
auto?: boolean;
|
|
22
|
-
/** View ratio, e.g. "16 / 9". Default 16 / 10. */
|
|
23
|
-
ratio?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** `figure.oc-browser-frame`: fake browser chrome around a screenshot or any content. */
|
|
27
|
-
export const BrowserFrame = forwardRef<HTMLElement, BrowserFrameProps>(function BrowserFrame(
|
|
28
|
-
{ url, src, alt = "", caption, actions, minimal, dark, tilt, auto, ratio, className, style, children, ...rest },
|
|
29
|
-
ref,
|
|
30
|
-
) {
|
|
31
|
-
const vars = ratio ? ({ "--oc-browser-frame-ratio": ratio } as CSSProperties) : undefined;
|
|
32
|
-
return (
|
|
33
|
-
<figure
|
|
34
|
-
ref={ref}
|
|
35
|
-
className={cx("oc-browser-frame", minimal && "oc-browser-frame--minimal", dark && "oc-browser-frame--dark", tilt && "oc-browser-frame--tilt", auto && "oc-browser-frame--auto", className)}
|
|
36
|
-
style={{ ...vars, ...style }}
|
|
37
|
-
{...rest}
|
|
38
|
-
>
|
|
39
|
-
<div className="oc-browser-frame__bar">
|
|
40
|
-
<i /><i /><i />
|
|
41
|
-
{url != null && <span className="oc-browser-frame__address">{url}</span>}
|
|
42
|
-
{actions != null && <span className="oc-browser-frame__actions" aria-hidden="true">{actions}</span>}
|
|
43
|
-
</div>
|
|
44
|
-
<div className="oc-browser-frame__view">{children ?? (src ? <img src={src} alt={alt} /> : <div className="oc-media-art" role="img" aria-label={alt} />)}</div>
|
|
45
|
-
{caption != null && <figcaption className="oc-browser-frame__caption">{caption}</figcaption>}
|
|
46
|
-
</figure>
|
|
47
|
-
);
|
|
48
|
-
});
|
|
1
|
+
// File: packages/react/src/components/BrowserFrame.tsx
|
|
2
|
+
// Map: props -> BrowserFrame (figure -> bar: dots, address, actions -> view (img or children) -> caption)
|
|
3
|
+
// Same markup as packages/ui/snippets/browser-frame.html.
|
|
4
|
+
import { forwardRef } from "react";
|
|
5
|
+
import type { CSSProperties, HTMLAttributes, ReactNode } from "react";
|
|
6
|
+
import { cx } from "../utils";
|
|
7
|
+
|
|
8
|
+
export interface BrowserFrameProps extends HTMLAttributes<HTMLElement> {
|
|
9
|
+
/** Text in the address pill. Leave out (or use `minimal`) for dots only. */
|
|
10
|
+
url?: string;
|
|
11
|
+
/** Screenshot URL. `children` replace it (live content). */
|
|
12
|
+
src?: string;
|
|
13
|
+
alt?: string;
|
|
14
|
+
caption?: ReactNode;
|
|
15
|
+
/** Small glyphs on the right of the bar. */
|
|
16
|
+
actions?: ReactNode;
|
|
17
|
+
minimal?: boolean;
|
|
18
|
+
dark?: boolean;
|
|
19
|
+
tilt?: boolean;
|
|
20
|
+
/** View height follows the content instead of the ratio. */
|
|
21
|
+
auto?: boolean;
|
|
22
|
+
/** View ratio, e.g. "16 / 9". Default 16 / 10. */
|
|
23
|
+
ratio?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** `figure.oc-browser-frame`: fake browser chrome around a screenshot or any content. */
|
|
27
|
+
export const BrowserFrame = forwardRef<HTMLElement, BrowserFrameProps>(function BrowserFrame(
|
|
28
|
+
{ url, src, alt = "", caption, actions, minimal, dark, tilt, auto, ratio, className, style, children, ...rest },
|
|
29
|
+
ref,
|
|
30
|
+
) {
|
|
31
|
+
const vars = ratio ? ({ "--oc-browser-frame-ratio": ratio } as CSSProperties) : undefined;
|
|
32
|
+
return (
|
|
33
|
+
<figure
|
|
34
|
+
ref={ref}
|
|
35
|
+
className={cx("oc-browser-frame", minimal && "oc-browser-frame--minimal", dark && "oc-browser-frame--dark", tilt && "oc-browser-frame--tilt", auto && "oc-browser-frame--auto", className)}
|
|
36
|
+
style={{ ...vars, ...style }}
|
|
37
|
+
{...rest}
|
|
38
|
+
>
|
|
39
|
+
<div className="oc-browser-frame__bar">
|
|
40
|
+
<i /><i /><i />
|
|
41
|
+
{url != null && <span className="oc-browser-frame__address">{url}</span>}
|
|
42
|
+
{actions != null && <span className="oc-browser-frame__actions" aria-hidden="true">{actions}</span>}
|
|
43
|
+
</div>
|
|
44
|
+
<div className="oc-browser-frame__view">{children ?? (src ? <img src={src} alt={alt} /> : <div className="oc-media-art" role="img" aria-label={alt} />)}</div>
|
|
45
|
+
{caption != null && <figcaption className="oc-browser-frame__caption">{caption}</figcaption>}
|
|
46
|
+
</figure>
|
|
47
|
+
);
|
|
48
|
+
});
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
// File: packages/react/src/components/BusyOverlay.tsx
|
|
2
|
-
// Map: types -> BusyOverlay (<dialog closedby="none">: spinner or progress, title, text)
|
|
3
|
-
// Same markup as packages/ui/snippets/busy-overlay.html. Open it with `open` while a long submit or upload runs;
|
|
4
|
-
// the dialog is modal (the page is inert) and Esc does not close it. The 300ms delay before it shows is CSS.
|
|
5
|
-
"use client";
|
|
6
|
-
|
|
7
|
-
import { forwardRef, useId, useRef } from "react";
|
|
8
|
-
import type { DialogHTMLAttributes, ReactNode } from "react";
|
|
9
|
-
import { attrs, cx } from "../utils";
|
|
10
|
-
import { useDialog, useMergedRef } from "../hooks";
|
|
11
|
-
import type { DialogState } from "../hooks";
|
|
12
|
-
import { Progress } from "./Progress";
|
|
13
|
-
|
|
14
|
-
export interface BusyOverlayProps extends Omit<DialogHTMLAttributes<HTMLDialogElement>, "title" | "open" | "onClose">, DialogState {
|
|
15
|
-
title?: ReactNode;
|
|
16
|
-
text?: ReactNode;
|
|
17
|
-
/** Known percentage: shows `.oc-progress` instead of the spinner. */
|
|
18
|
-
progress?: number;
|
|
19
|
-
/** aria-label of the spinner / progress bar (default "Busy"). */
|
|
20
|
-
label?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** `dialog.oc-busy-overlay[closedby=none]`. Controlled with `open` / `onOpenChange`, or uncontrolled. */
|
|
24
|
-
export const BusyOverlay = forwardRef<HTMLDialogElement, BusyOverlayProps>(function BusyOverlay(
|
|
25
|
-
{
|
|
26
|
-
title = "Saving…", text = "The photos are being processed. This can take a minute; please keep this page open.",
|
|
27
|
-
progress, label = "Busy", open, defaultOpen, onOpenChange, id, className, ...rest
|
|
28
|
-
},
|
|
29
|
-
forwardedRef,
|
|
30
|
-
) {
|
|
31
|
-
const inner = useRef<HTMLDialogElement>(null);
|
|
32
|
-
const ref = useMergedRef(inner, forwardedRef);
|
|
33
|
-
const dlg = useDialog(inner, { open, defaultOpen, onOpenChange });
|
|
34
|
-
const auto = useId().replace(/[^\w-]/g, "");
|
|
35
|
-
const base = id ?? `oc-busy-overlay-${auto}`;
|
|
36
|
-
return (
|
|
37
|
-
<dialog
|
|
38
|
-
ref={ref}
|
|
39
|
-
className={cx("oc-busy-overlay", className)}
|
|
40
|
-
id={base}
|
|
41
|
-
{...attrs({ closedby: "none" })}
|
|
42
|
-
aria-labelledby={`${base}-title`}
|
|
43
|
-
aria-describedby={text != null ? `${base}-text` : undefined}
|
|
44
|
-
{...rest}
|
|
45
|
-
onClose={dlg.onClose}
|
|
46
|
-
>
|
|
47
|
-
{progress == null ? <span className="oc-spinner" role="status" aria-label={label} /> : <Progress value={progress} aria-label={label} />}
|
|
48
|
-
<h2 className="oc-busy-overlay__title" id={`${base}-title`}>{title}</h2>
|
|
49
|
-
{text != null && <p className="oc-busy-overlay__text" id={`${base}-text`}>{text}</p>}
|
|
50
|
-
</dialog>
|
|
51
|
-
);
|
|
52
|
-
});
|
|
1
|
+
// File: packages/react/src/components/BusyOverlay.tsx
|
|
2
|
+
// Map: types -> BusyOverlay (<dialog closedby="none">: spinner or progress, title, text)
|
|
3
|
+
// Same markup as packages/ui/snippets/busy-overlay.html. Open it with `open` while a long submit or upload runs;
|
|
4
|
+
// the dialog is modal (the page is inert) and Esc does not close it. The 300ms delay before it shows is CSS.
|
|
5
|
+
"use client";
|
|
6
|
+
|
|
7
|
+
import { forwardRef, useId, useRef } from "react";
|
|
8
|
+
import type { DialogHTMLAttributes, ReactNode } from "react";
|
|
9
|
+
import { attrs, cx } from "../utils";
|
|
10
|
+
import { useDialog, useMergedRef } from "../hooks";
|
|
11
|
+
import type { DialogState } from "../hooks";
|
|
12
|
+
import { Progress } from "./Progress";
|
|
13
|
+
|
|
14
|
+
export interface BusyOverlayProps extends Omit<DialogHTMLAttributes<HTMLDialogElement>, "title" | "open" | "onClose">, DialogState {
|
|
15
|
+
title?: ReactNode;
|
|
16
|
+
text?: ReactNode;
|
|
17
|
+
/** Known percentage: shows `.oc-progress` instead of the spinner. */
|
|
18
|
+
progress?: number;
|
|
19
|
+
/** aria-label of the spinner / progress bar (default "Busy"). */
|
|
20
|
+
label?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** `dialog.oc-busy-overlay[closedby=none]`. Controlled with `open` / `onOpenChange`, or uncontrolled. */
|
|
24
|
+
export const BusyOverlay = forwardRef<HTMLDialogElement, BusyOverlayProps>(function BusyOverlay(
|
|
25
|
+
{
|
|
26
|
+
title = "Saving…", text = "The photos are being processed. This can take a minute; please keep this page open.",
|
|
27
|
+
progress, label = "Busy", open, defaultOpen, onOpenChange, id, className, ...rest
|
|
28
|
+
},
|
|
29
|
+
forwardedRef,
|
|
30
|
+
) {
|
|
31
|
+
const inner = useRef<HTMLDialogElement>(null);
|
|
32
|
+
const ref = useMergedRef(inner, forwardedRef);
|
|
33
|
+
const dlg = useDialog(inner, { open, defaultOpen, onOpenChange });
|
|
34
|
+
const auto = useId().replace(/[^\w-]/g, "");
|
|
35
|
+
const base = id ?? `oc-busy-overlay-${auto}`;
|
|
36
|
+
return (
|
|
37
|
+
<dialog
|
|
38
|
+
ref={ref}
|
|
39
|
+
className={cx("oc-busy-overlay", className)}
|
|
40
|
+
id={base}
|
|
41
|
+
{...attrs({ closedby: "none" })}
|
|
42
|
+
aria-labelledby={`${base}-title`}
|
|
43
|
+
aria-describedby={text != null ? `${base}-text` : undefined}
|
|
44
|
+
{...rest}
|
|
45
|
+
onClose={dlg.onClose}
|
|
46
|
+
>
|
|
47
|
+
{progress == null ? <span className="oc-spinner" role="status" aria-label={label} /> : <Progress value={progress} aria-label={label} />}
|
|
48
|
+
<h2 className="oc-busy-overlay__title" id={`${base}-title`}>{title}</h2>
|
|
49
|
+
{text != null && <p className="oc-busy-overlay__text" id={`${base}-text`}>{text}</p>}
|
|
50
|
+
</dialog>
|
|
51
|
+
);
|
|
52
|
+
});
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import { forwardRef } from "react";
|
|
2
|
-
import type { AnchorHTMLAttributes, ButtonHTMLAttributes } from "react";
|
|
3
|
-
import { cx } from "../utils";
|
|
4
|
-
|
|
5
|
-
export type ButtonVariant = "default" | "secondary" | "outline" | "ghost" | "link" | "destructive" | "success" | "gradient" | "glass";
|
|
6
|
-
export type ButtonSize = "xs" | "sm" | "md" | "lg";
|
|
7
|
-
|
|
8
|
-
interface ButtonStyleProps {
|
|
9
|
-
/** Visual variant (`default` = primary). */
|
|
10
|
-
variant?: ButtonVariant;
|
|
11
|
-
/** `md` is the default size and adds no class. */
|
|
12
|
-
size?: ButtonSize;
|
|
13
|
-
/** Square icon-only button. */
|
|
14
|
-
icon?: boolean;
|
|
15
|
-
/** Fully rounded pill shape. */
|
|
16
|
-
pill?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** The class string of a button; reuse it on your own elements (router links etc.). */
|
|
20
|
-
export function buttonClass({ variant = "default", size = "md", icon, pill, className }: ButtonStyleProps & { className?: string }) {
|
|
21
|
-
return cx(
|
|
22
|
-
"oc-btn",
|
|
23
|
-
variant !== "default" && `oc-btn--${variant}`,
|
|
24
|
-
size !== "md" && `oc-btn--${size}`,
|
|
25
|
-
icon && "oc-btn--icon",
|
|
26
|
-
pill && "oc-btn--pill",
|
|
27
|
-
className,
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, ButtonStyleProps {}
|
|
32
|
-
|
|
33
|
-
/** `<button class="oc-btn oc-btn--{variant} oc-btn--{size}">` */
|
|
34
|
-
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
35
|
-
{ variant, size, icon, pill, className, ...rest },
|
|
36
|
-
ref,
|
|
37
|
-
) {
|
|
38
|
-
return <button ref={ref} className={buttonClass({ variant, size, icon, pill, className })} {...rest} />;
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
export interface ButtonLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement>, ButtonStyleProps {}
|
|
42
|
-
|
|
43
|
-
/** An anchor styled as a button. */
|
|
44
|
-
export const ButtonLink = forwardRef<HTMLAnchorElement, ButtonLinkProps>(function ButtonLink(
|
|
45
|
-
{ variant, size, icon, pill, className, ...rest },
|
|
46
|
-
ref,
|
|
47
|
-
) {
|
|
48
|
-
return <a ref={ref} className={buttonClass({ variant, size, icon, pill, className })} {...rest} />;
|
|
49
|
-
});
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
import type { AnchorHTMLAttributes, ButtonHTMLAttributes } from "react";
|
|
3
|
+
import { cx } from "../utils";
|
|
4
|
+
|
|
5
|
+
export type ButtonVariant = "default" | "secondary" | "outline" | "ghost" | "link" | "destructive" | "success" | "gradient" | "glass";
|
|
6
|
+
export type ButtonSize = "xs" | "sm" | "md" | "lg";
|
|
7
|
+
|
|
8
|
+
interface ButtonStyleProps {
|
|
9
|
+
/** Visual variant (`default` = primary). */
|
|
10
|
+
variant?: ButtonVariant;
|
|
11
|
+
/** `md` is the default size and adds no class. */
|
|
12
|
+
size?: ButtonSize;
|
|
13
|
+
/** Square icon-only button. */
|
|
14
|
+
icon?: boolean;
|
|
15
|
+
/** Fully rounded pill shape. */
|
|
16
|
+
pill?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The class string of a button; reuse it on your own elements (router links etc.). */
|
|
20
|
+
export function buttonClass({ variant = "default", size = "md", icon, pill, className }: ButtonStyleProps & { className?: string }) {
|
|
21
|
+
return cx(
|
|
22
|
+
"oc-btn",
|
|
23
|
+
variant !== "default" && `oc-btn--${variant}`,
|
|
24
|
+
size !== "md" && `oc-btn--${size}`,
|
|
25
|
+
icon && "oc-btn--icon",
|
|
26
|
+
pill && "oc-btn--pill",
|
|
27
|
+
className,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, ButtonStyleProps {}
|
|
32
|
+
|
|
33
|
+
/** `<button class="oc-btn oc-btn--{variant} oc-btn--{size}">` */
|
|
34
|
+
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
35
|
+
{ variant, size, icon, pill, className, ...rest },
|
|
36
|
+
ref,
|
|
37
|
+
) {
|
|
38
|
+
return <button ref={ref} className={buttonClass({ variant, size, icon, pill, className })} {...rest} />;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export interface ButtonLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement>, ButtonStyleProps {}
|
|
42
|
+
|
|
43
|
+
/** An anchor styled as a button. */
|
|
44
|
+
export const ButtonLink = forwardRef<HTMLAnchorElement, ButtonLinkProps>(function ButtonLink(
|
|
45
|
+
{ variant, size, icon, pill, className, ...rest },
|
|
46
|
+
ref,
|
|
47
|
+
) {
|
|
48
|
+
return <a ref={ref} className={buttonClass({ variant, size, icon, pill, className })} {...rest} />;
|
|
49
|
+
});
|
package/src/components/Card.tsx
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { forwardRef } from "react";
|
|
2
|
-
import type { HTMLAttributes } from "react";
|
|
3
|
-
import { cx } from "../utils";
|
|
4
|
-
|
|
5
|
-
type Tag = "div" | "h3" | "p";
|
|
6
|
-
|
|
7
|
-
function part<E extends HTMLElement>(tag: Tag, base: string, displayName: string) {
|
|
8
|
-
const Comp = forwardRef<E, HTMLAttributes<E>>(function Part({ className, ...rest }, ref) {
|
|
9
|
-
const Element = tag as "div";
|
|
10
|
-
return <Element ref={ref as React.Ref<HTMLDivElement>} className={cx(base, className)} {...(rest as HTMLAttributes<HTMLDivElement>)} />;
|
|
11
|
-
});
|
|
12
|
-
Comp.displayName = displayName;
|
|
13
|
-
return Comp;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** `.oc-card` */
|
|
17
|
-
export const Card = part<HTMLDivElement>("div", "oc-card", "Card");
|
|
18
|
-
/** `.oc-card-header` */
|
|
19
|
-
export const CardHeader = part<HTMLDivElement>("div", "oc-card-header", "CardHeader");
|
|
20
|
-
/** `h3.oc-card-title` */
|
|
21
|
-
export const CardTitle = part<HTMLHeadingElement>("h3", "oc-card-title", "CardTitle");
|
|
22
|
-
/** `p.oc-card-description` */
|
|
23
|
-
export const CardDescription = part<HTMLParagraphElement>("p", "oc-card-description", "CardDescription");
|
|
24
|
-
/** `.oc-card-content` */
|
|
25
|
-
export const CardContent = part<HTMLDivElement>("div", "oc-card-content", "CardContent");
|
|
26
|
-
/** `.oc-card-footer` */
|
|
27
|
-
export const CardFooter = part<HTMLDivElement>("div", "oc-card-footer", "CardFooter");
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
import type { HTMLAttributes } from "react";
|
|
3
|
+
import { cx } from "../utils";
|
|
4
|
+
|
|
5
|
+
type Tag = "div" | "h3" | "p";
|
|
6
|
+
|
|
7
|
+
function part<E extends HTMLElement>(tag: Tag, base: string, displayName: string) {
|
|
8
|
+
const Comp = forwardRef<E, HTMLAttributes<E>>(function Part({ className, ...rest }, ref) {
|
|
9
|
+
const Element = tag as "div";
|
|
10
|
+
return <Element ref={ref as React.Ref<HTMLDivElement>} className={cx(base, className)} {...(rest as HTMLAttributes<HTMLDivElement>)} />;
|
|
11
|
+
});
|
|
12
|
+
Comp.displayName = displayName;
|
|
13
|
+
return Comp;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** `.oc-card` */
|
|
17
|
+
export const Card = part<HTMLDivElement>("div", "oc-card", "Card");
|
|
18
|
+
/** `.oc-card-header` */
|
|
19
|
+
export const CardHeader = part<HTMLDivElement>("div", "oc-card-header", "CardHeader");
|
|
20
|
+
/** `h3.oc-card-title` */
|
|
21
|
+
export const CardTitle = part<HTMLHeadingElement>("h3", "oc-card-title", "CardTitle");
|
|
22
|
+
/** `p.oc-card-description` */
|
|
23
|
+
export const CardDescription = part<HTMLParagraphElement>("p", "oc-card-description", "CardDescription");
|
|
24
|
+
/** `.oc-card-content` */
|
|
25
|
+
export const CardContent = part<HTMLDivElement>("div", "oc-card-content", "CardContent");
|
|
26
|
+
/** `.oc-card-footer` */
|
|
27
|
+
export const CardFooter = part<HTMLDivElement>("div", "oc-card-footer", "CardFooter");
|