@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.
Files changed (143) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +21 -21
  3. package/package.json +71 -71
  4. package/src/blocks/BlockAboutTimeline.tsx +46 -46
  5. package/src/blocks/BlockAppHeader.tsx +52 -52
  6. package/src/blocks/BlockAppShell.tsx +56 -56
  7. package/src/blocks/BlockAuth.tsx +53 -53
  8. package/src/blocks/BlockAuthSplit.tsx +84 -84
  9. package/src/blocks/BlockContactGrid.tsx +71 -71
  10. package/src/blocks/BlockCta.tsx +23 -23
  11. package/src/blocks/BlockDataTable.tsx +57 -57
  12. package/src/blocks/BlockFaq.tsx +55 -55
  13. package/src/blocks/BlockFeatures.tsx +38 -38
  14. package/src/blocks/BlockFooter.tsx +50 -50
  15. package/src/blocks/BlockFormPage.tsx +26 -26
  16. package/src/blocks/BlockHero.tsx +27 -27
  17. package/src/blocks/BlockHeroCode.tsx +63 -63
  18. package/src/blocks/BlockHeroOrbit.tsx +73 -73
  19. package/src/blocks/BlockHeroSplit.tsx +28 -28
  20. package/src/blocks/BlockModuleHeader.tsx +34 -34
  21. package/src/blocks/BlockPageHero.tsx +25 -25
  22. package/src/blocks/BlockPortfolioGrid.tsx +50 -50
  23. package/src/blocks/BlockPricing.tsx +62 -62
  24. package/src/blocks/BlockProcess.tsx +50 -50
  25. package/src/blocks/BlockProductGrid.tsx +54 -54
  26. package/src/blocks/BlockProfileHeader.tsx +57 -57
  27. package/src/blocks/BlockShowcase.tsx +58 -58
  28. package/src/blocks/BlockSiteFooter.tsx +60 -60
  29. package/src/blocks/BlockSiteHeader.tsx +42 -42
  30. package/src/blocks/BlockSpotlight.tsx +58 -58
  31. package/src/blocks/BlockStats.tsx +26 -26
  32. package/src/blocks/BlockStatsCounter.tsx +31 -31
  33. package/src/blocks/BlockTeamList.tsx +48 -48
  34. package/src/blocks/BlockTwoFactor.tsx +57 -57
  35. package/src/blocks/BlockWizardDialog.tsx +101 -101
  36. package/src/components/Accordion.tsx +31 -31
  37. package/src/components/AccordionAccent.tsx +39 -39
  38. package/src/components/AffixInput.tsx +47 -47
  39. package/src/components/Alert.tsx +24 -24
  40. package/src/components/AlertDialog.tsx +60 -60
  41. package/src/components/AlphaTabs.tsx +63 -63
  42. package/src/components/Attachment.tsx +140 -140
  43. package/src/components/Avatar.tsx +32 -32
  44. package/src/components/BackLink.tsx +11 -11
  45. package/src/components/Badge.tsx +14 -14
  46. package/src/components/BarcodeInput.tsx +64 -64
  47. package/src/components/Breadcrumb.tsx +32 -32
  48. package/src/components/BrowserFrame.tsx +48 -48
  49. package/src/components/BusyOverlay.tsx +52 -52
  50. package/src/components/Button.tsx +49 -49
  51. package/src/components/Card.tsx +27 -27
  52. package/src/components/ChartLine.tsx +119 -119
  53. package/src/components/ChatComposer.tsx +73 -73
  54. package/src/components/Checkbox.tsx +14 -14
  55. package/src/components/Checklist.tsx +45 -45
  56. package/src/components/ChipFilter.tsx +60 -60
  57. package/src/components/Collapsible.tsx +43 -43
  58. package/src/components/ColorField.tsx +48 -48
  59. package/src/components/ColorSelect.tsx +49 -49
  60. package/src/components/ColumnMenu.tsx +66 -66
  61. package/src/components/Command.tsx +113 -113
  62. package/src/components/ConfirmDialog.tsx +95 -95
  63. package/src/components/CookieConsent.tsx +137 -137
  64. package/src/components/CopyField.tsx +44 -44
  65. package/src/components/Counter.tsx +21 -21
  66. package/src/components/DataTable.tsx +201 -201
  67. package/src/components/DecimalInput.tsx +87 -87
  68. package/src/components/DefinitionList.tsx +38 -38
  69. package/src/components/DocViewer.tsx +110 -110
  70. package/src/components/Dropdown.tsx +52 -52
  71. package/src/components/Dropzone.tsx +46 -46
  72. package/src/components/EmojiPicker.tsx +72 -72
  73. package/src/components/EmptyState.tsx +27 -27
  74. package/src/components/EntityCard.tsx +56 -56
  75. package/src/components/FactList.tsx +43 -43
  76. package/src/components/FilterCounts.tsx +50 -50
  77. package/src/components/FlipCard.tsx +55 -55
  78. package/src/components/FormField.tsx +54 -54
  79. package/src/components/Greeting.tsx +63 -63
  80. package/src/components/Heading.tsx +27 -27
  81. package/src/components/Icon.tsx +25 -25
  82. package/src/components/ImageZoom.tsx +51 -51
  83. package/src/components/Input.tsx +21 -21
  84. package/src/components/InputGroup.tsx +24 -24
  85. package/src/components/LanguageSelect.tsx +64 -64
  86. package/src/components/LauncherCard.tsx +50 -50
  87. package/src/components/Lightbox.tsx +83 -83
  88. package/src/components/LinkArrow.tsx +27 -27
  89. package/src/components/ListToolbar.tsx +67 -67
  90. package/src/components/Marquee.tsx +38 -38
  91. package/src/components/MediaCard.tsx +47 -47
  92. package/src/components/MediaRow.tsx +53 -53
  93. package/src/components/MediaThumb.tsx +49 -49
  94. package/src/components/Message.tsx +171 -171
  95. package/src/components/MessageThread.tsx +177 -177
  96. package/src/components/Modal.tsx +81 -81
  97. package/src/components/Navmenu.tsx +62 -62
  98. package/src/components/NextTeaser.tsx +29 -29
  99. package/src/components/NumberInput.tsx +64 -64
  100. package/src/components/OtpInput.tsx +48 -48
  101. package/src/components/PageHeader.tsx +43 -43
  102. package/src/components/Pagination.tsx +42 -42
  103. package/src/components/PasswordInput.tsx +36 -36
  104. package/src/components/PasswordRequirements.tsx +46 -46
  105. package/src/components/PhoneInput.tsx +93 -93
  106. package/src/components/Popover.tsx +34 -34
  107. package/src/components/PriceCard.tsx +83 -83
  108. package/src/components/ProductMark.tsx +35 -35
  109. package/src/components/Progress.tsx +32 -32
  110. package/src/components/ProjectCard.tsx +46 -46
  111. package/src/components/Prose.tsx +79 -79
  112. package/src/components/RecoveryCodes.tsx +65 -65
  113. package/src/components/Segmented.tsx +47 -47
  114. package/src/components/Select.tsx +27 -27
  115. package/src/components/SeverityCard.tsx +53 -53
  116. package/src/components/Sheet.tsx +62 -62
  117. package/src/components/Skeleton.tsx +12 -12
  118. package/src/components/SortableRow.tsx +46 -46
  119. package/src/components/StackCard.tsx +40 -40
  120. package/src/components/StackList.tsx +42 -42
  121. package/src/components/StatCard.tsx +49 -49
  122. package/src/components/Stepper.tsx +54 -54
  123. package/src/components/Switch.tsx +18 -18
  124. package/src/components/Table.tsx +75 -75
  125. package/src/components/TableActions.tsx +54 -54
  126. package/src/components/TableCompact.tsx +61 -61
  127. package/src/components/Tabs.tsx +45 -45
  128. package/src/components/TagInput.tsx +73 -73
  129. package/src/components/TerminalLog.tsx +63 -63
  130. package/src/components/Textarea.tsx +19 -19
  131. package/src/components/ThemeSwitch.tsx +45 -45
  132. package/src/components/TimeInput.tsx +76 -76
  133. package/src/components/Timeline.tsx +45 -45
  134. package/src/components/Toast.tsx +138 -138
  135. package/src/components/ToggleField.tsx +39 -39
  136. package/src/components/Tooltip.tsx +14 -14
  137. package/src/components/TopProgress.tsx +54 -54
  138. package/src/components/Tree.tsx +97 -97
  139. package/src/components/UpgradeBanner.tsx +62 -62
  140. package/src/components/User.tsx +31 -31
  141. package/src/components/VersionBanner.tsx +41 -41
  142. package/src/hooks.tsx +141 -141
  143. package/src/utils.ts +25 -25
@@ -1,27 +1,27 @@
1
- import { forwardRef } from "react";
2
- import type { HTMLAttributes, ReactNode } from "react";
3
- import { cx } from "../utils";
4
-
5
- export interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
6
- icon?: ReactNode;
7
- title: ReactNode;
8
- /** Explanation (`oc-empty-text`). */
9
- description?: ReactNode;
10
- /** Buttons row (`oc-empty-actions`). */
11
- actions?: ReactNode;
12
- }
13
-
14
- /** `<div class="oc-empty">` for lists without data. */
15
- export const EmptyState = forwardRef<HTMLDivElement, EmptyStateProps>(function EmptyState(
16
- { icon, title, description, actions, className, ...rest },
17
- ref,
18
- ) {
19
- return (
20
- <div ref={ref} className={cx("oc-empty", className)} {...rest}>
21
- {icon != null && <span className="oc-empty-icon" aria-hidden="true">{icon}</span>}
22
- <h3 className="oc-empty-title">{title}</h3>
23
- {description != null && <p className="oc-empty-text">{description}</p>}
24
- {actions != null && <div className="oc-empty-actions">{actions}</div>}
25
- </div>
26
- );
27
- });
1
+ import { forwardRef } from "react";
2
+ import type { HTMLAttributes, ReactNode } from "react";
3
+ import { cx } from "../utils";
4
+
5
+ export interface EmptyStateProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
6
+ icon?: ReactNode;
7
+ title: ReactNode;
8
+ /** Explanation (`oc-empty-text`). */
9
+ description?: ReactNode;
10
+ /** Buttons row (`oc-empty-actions`). */
11
+ actions?: ReactNode;
12
+ }
13
+
14
+ /** `<div class="oc-empty">` for lists without data. */
15
+ export const EmptyState = forwardRef<HTMLDivElement, EmptyStateProps>(function EmptyState(
16
+ { icon, title, description, actions, className, ...rest },
17
+ ref,
18
+ ) {
19
+ return (
20
+ <div ref={ref} className={cx("oc-empty", className)} {...rest}>
21
+ {icon != null && <span className="oc-empty-icon" aria-hidden="true">{icon}</span>}
22
+ <h3 className="oc-empty-title">{title}</h3>
23
+ {description != null && <p className="oc-empty-text">{description}</p>}
24
+ {actions != null && <div className="oc-empty-actions">{actions}</div>}
25
+ </div>
26
+ );
27
+ });
@@ -1,56 +1,56 @@
1
- // File: packages/react/src/components/EntityCard.tsx
2
- // Map: props -> EntityCard (grid: frame (img / glyph / media) | body (eyebrow, title, description, facts, children) -> footer)
3
- // Same markup as packages/ui/snippets/entity-card.html.
4
- import { Fragment, forwardRef } from "react";
5
- import type { HTMLAttributes, ReactNode } from "react";
6
- import { cx } from "../utils";
7
-
8
- export interface EntityCardProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
9
- title: ReactNode;
10
- eyebrow?: ReactNode;
11
- description?: ReactNode;
12
- /** Picture in the frame. */
13
- src?: string;
14
- alt?: string;
15
- /** Large character or icon in the frame (when there is no `src`). */
16
- glyph?: ReactNode;
17
- /** Anything else for the frame (svg, placeholder art). Wins over src / glyph. */
18
- media?: ReactNode;
19
- /** Keep a margin around a logo / symbol picture (`__frame--contain`). */
20
- contain?: boolean;
21
- /** Key / value facts, rendered as a <dl>. */
22
- facts?: Array<[ReactNode, ReactNode]>;
23
- /** Tags or actions under a divider. */
24
- footer?: ReactNode;
25
- stacked?: boolean;
26
- size?: "sm" | "md";
27
- }
28
-
29
- /** `article.oc-entity-card`: framed media cell + detail column. */
30
- export const EntityCard = forwardRef<HTMLElement, EntityCardProps>(function EntityCard(
31
- { title, eyebrow, description, src, alt = "", glyph, media, contain, facts, footer, stacked, size = "md", className, children, ...rest },
32
- ref,
33
- ) {
34
- const frame = media ?? (src ? <img src={src} alt={alt} /> : glyph != null ? <span className="oc-entity-card__glyph" aria-hidden="true">{glyph}</span> : null);
35
- return (
36
- <article ref={ref} className={cx("oc-entity-card", stacked && "oc-entity-card--stacked", size === "sm" && "oc-entity-card--sm", className)} {...rest}>
37
- <div className="oc-entity-card__grid">
38
- <div className={cx("oc-entity-card__frame", contain && "oc-entity-card__frame--contain")}>{frame}</div>
39
- <div className="oc-entity-card__body">
40
- {eyebrow != null && <p className="oc-entity-card__eyebrow">{eyebrow}</p>}
41
- <h3 className="oc-entity-card__title">{title}</h3>
42
- {description != null && <p className="oc-entity-card__description">{description}</p>}
43
- {facts && facts.length > 0 && (
44
- <dl className="oc-entity-card__facts">
45
- {facts.map(([k, v], i) => (
46
- <Fragment key={i}><dt>{k}</dt><dd>{v}</dd></Fragment>
47
- ))}
48
- </dl>
49
- )}
50
- {children}
51
- </div>
52
- </div>
53
- {footer != null && <div className="oc-entity-card__footer">{footer}</div>}
54
- </article>
55
- );
56
- });
1
+ // File: packages/react/src/components/EntityCard.tsx
2
+ // Map: props -> EntityCard (grid: frame (img / glyph / media) | body (eyebrow, title, description, facts, children) -> footer)
3
+ // Same markup as packages/ui/snippets/entity-card.html.
4
+ import { Fragment, forwardRef } from "react";
5
+ import type { HTMLAttributes, ReactNode } from "react";
6
+ import { cx } from "../utils";
7
+
8
+ export interface EntityCardProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
9
+ title: ReactNode;
10
+ eyebrow?: ReactNode;
11
+ description?: ReactNode;
12
+ /** Picture in the frame. */
13
+ src?: string;
14
+ alt?: string;
15
+ /** Large character or icon in the frame (when there is no `src`). */
16
+ glyph?: ReactNode;
17
+ /** Anything else for the frame (svg, placeholder art). Wins over src / glyph. */
18
+ media?: ReactNode;
19
+ /** Keep a margin around a logo / symbol picture (`__frame--contain`). */
20
+ contain?: boolean;
21
+ /** Key / value facts, rendered as a <dl>. */
22
+ facts?: Array<[ReactNode, ReactNode]>;
23
+ /** Tags or actions under a divider. */
24
+ footer?: ReactNode;
25
+ stacked?: boolean;
26
+ size?: "sm" | "md";
27
+ }
28
+
29
+ /** `article.oc-entity-card`: framed media cell + detail column. */
30
+ export const EntityCard = forwardRef<HTMLElement, EntityCardProps>(function EntityCard(
31
+ { title, eyebrow, description, src, alt = "", glyph, media, contain, facts, footer, stacked, size = "md", className, children, ...rest },
32
+ ref,
33
+ ) {
34
+ const frame = media ?? (src ? <img src={src} alt={alt} /> : glyph != null ? <span className="oc-entity-card__glyph" aria-hidden="true">{glyph}</span> : null);
35
+ return (
36
+ <article ref={ref} className={cx("oc-entity-card", stacked && "oc-entity-card--stacked", size === "sm" && "oc-entity-card--sm", className)} {...rest}>
37
+ <div className="oc-entity-card__grid">
38
+ <div className={cx("oc-entity-card__frame", contain && "oc-entity-card__frame--contain")}>{frame}</div>
39
+ <div className="oc-entity-card__body">
40
+ {eyebrow != null && <p className="oc-entity-card__eyebrow">{eyebrow}</p>}
41
+ <h3 className="oc-entity-card__title">{title}</h3>
42
+ {description != null && <p className="oc-entity-card__description">{description}</p>}
43
+ {facts && facts.length > 0 && (
44
+ <dl className="oc-entity-card__facts">
45
+ {facts.map(([k, v], i) => (
46
+ <Fragment key={i}><dt>{k}</dt><dd>{v}</dd></Fragment>
47
+ ))}
48
+ </dl>
49
+ )}
50
+ {children}
51
+ </div>
52
+ </div>
53
+ {footer != null && <div className="oc-entity-card__footer">{footer}</div>}
54
+ </article>
55
+ );
56
+ });
@@ -1,43 +1,43 @@
1
- // File: packages/react/src/components/FactList.tsx | Map: types -> FactList (dl.oc-fact-list -> div > dt + dd)
2
- import { forwardRef } from "react";
3
- import type { HTMLAttributes, ReactNode } from "react";
4
- import { cx } from "../utils";
5
-
6
- export interface FactListRow {
7
- label: ReactNode;
8
- value: ReactNode;
9
- }
10
-
11
- export interface FactListProps extends HTMLAttributes<HTMLDListElement> {
12
- items: FactListRow[];
13
- plain?: boolean;
14
- compact?: boolean;
15
- stacked?: boolean;
16
- /** Values in the accent color. */
17
- accent?: boolean;
18
- }
19
-
20
- /** `<dl class="oc-fact-list">`: key / value product facts (variant of the definition list). */
21
- export const FactList = forwardRef<HTMLDListElement, FactListProps>(function FactList(
22
- { items, plain = false, compact = false, stacked = false, accent = false, className, ...rest },
23
- ref,
24
- ) {
25
- const classes = cx(
26
- "oc-fact-list",
27
- plain && "oc-fact-list--plain",
28
- compact && "oc-fact-list--compact",
29
- stacked && "oc-fact-list--stacked",
30
- accent && "oc-fact-list--accent",
31
- className,
32
- );
33
- return (
34
- <dl ref={ref} className={classes} {...rest}>
35
- {items.map((row, i) => (
36
- <div key={i}>
37
- <dt>{row.label}</dt>
38
- <dd>{row.value}</dd>
39
- </div>
40
- ))}
41
- </dl>
42
- );
43
- });
1
+ // File: packages/react/src/components/FactList.tsx | Map: types -> FactList (dl.oc-fact-list -> div > dt + dd)
2
+ import { forwardRef } from "react";
3
+ import type { HTMLAttributes, ReactNode } from "react";
4
+ import { cx } from "../utils";
5
+
6
+ export interface FactListRow {
7
+ label: ReactNode;
8
+ value: ReactNode;
9
+ }
10
+
11
+ export interface FactListProps extends HTMLAttributes<HTMLDListElement> {
12
+ items: FactListRow[];
13
+ plain?: boolean;
14
+ compact?: boolean;
15
+ stacked?: boolean;
16
+ /** Values in the accent color. */
17
+ accent?: boolean;
18
+ }
19
+
20
+ /** `<dl class="oc-fact-list">`: key / value product facts (variant of the definition list). */
21
+ export const FactList = forwardRef<HTMLDListElement, FactListProps>(function FactList(
22
+ { items, plain = false, compact = false, stacked = false, accent = false, className, ...rest },
23
+ ref,
24
+ ) {
25
+ const classes = cx(
26
+ "oc-fact-list",
27
+ plain && "oc-fact-list--plain",
28
+ compact && "oc-fact-list--compact",
29
+ stacked && "oc-fact-list--stacked",
30
+ accent && "oc-fact-list--accent",
31
+ className,
32
+ );
33
+ return (
34
+ <dl ref={ref} className={classes} {...rest}>
35
+ {items.map((row, i) => (
36
+ <div key={i}>
37
+ <dt>{row.label}</dt>
38
+ <dd>{row.value}</dd>
39
+ </div>
40
+ ))}
41
+ </dl>
42
+ );
43
+ });
@@ -1,50 +1,50 @@
1
- // File: packages/react/src/components/FilterCounts.tsx
2
- // Map: types -> component (fieldset radiogroup -> one tile per view: radio + value + label + hint)
3
- // Same markup as packages/ui/snippets/filter-counts.html.
4
- "use client";
5
-
6
- import { forwardRef, useId } from "react";
7
- import type { FieldsetHTMLAttributes, ReactNode } from "react";
8
- import { cx } from "../utils";
9
- import { useControllable } from "../hooks";
10
-
11
- export interface FilterCountsOption {
12
- value: string;
13
- label: ReactNode;
14
- count: number | string;
15
- hint?: ReactNode;
16
- /** Accent color of the tile; default primary. */
17
- tone?: "primary" | "success" | "destructive" | "muted";
18
- disabled?: boolean;
19
- }
20
-
21
- export interface FilterCountsProps extends Omit<FieldsetHTMLAttributes<HTMLFieldSetElement>, "onChange" | "defaultValue"> {
22
- options: FilterCountsOption[];
23
- value?: string;
24
- defaultValue?: string;
25
- onChange?: (value: string) => void;
26
- name?: string;
27
- /** Small inline pills instead of tiles. */
28
- compact?: boolean;
29
- }
30
-
31
- /** `.oc-filter-counts`: choose a view; every tile shows its item count. */
32
- export const FilterCounts = forwardRef<HTMLFieldSetElement, FilterCountsProps>(function FilterCounts(
33
- { options, value, defaultValue, onChange, name, compact, className, ...rest },
34
- ref,
35
- ) {
36
- const group = name ?? `oc-filter-${useId().replace(/:/g, "")}`;
37
- const [current, setCurrent] = useControllable<string>(value, defaultValue ?? options[0]?.value ?? "", onChange);
38
- return (
39
- <fieldset ref={ref} className={cx("oc-filter-counts", compact && "oc-filter-counts--compact", className)} role="radiogroup" {...rest}>
40
- {options.map((o) => (
41
- <label key={o.value} className={cx("oc-filter-counts-item", o.tone && o.tone !== "primary" && `oc-filter-counts-item--${o.tone}`)}>
42
- <input type="radio" name={group} value={o.value} disabled={o.disabled} checked={current === o.value} onChange={() => setCurrent(o.value)} />
43
- <span className="oc-filter-counts-value">{o.count}</span>
44
- <span className="oc-filter-counts-label">{o.label}</span>
45
- {o.hint != null && <span className="oc-filter-counts-hint">{o.hint}</span>}
46
- </label>
47
- ))}
48
- </fieldset>
49
- );
50
- });
1
+ // File: packages/react/src/components/FilterCounts.tsx
2
+ // Map: types -> component (fieldset radiogroup -> one tile per view: radio + value + label + hint)
3
+ // Same markup as packages/ui/snippets/filter-counts.html.
4
+ "use client";
5
+
6
+ import { forwardRef, useId } from "react";
7
+ import type { FieldsetHTMLAttributes, ReactNode } from "react";
8
+ import { cx } from "../utils";
9
+ import { useControllable } from "../hooks";
10
+
11
+ export interface FilterCountsOption {
12
+ value: string;
13
+ label: ReactNode;
14
+ count: number | string;
15
+ hint?: ReactNode;
16
+ /** Accent color of the tile; default primary. */
17
+ tone?: "primary" | "success" | "destructive" | "muted";
18
+ disabled?: boolean;
19
+ }
20
+
21
+ export interface FilterCountsProps extends Omit<FieldsetHTMLAttributes<HTMLFieldSetElement>, "onChange" | "defaultValue"> {
22
+ options: FilterCountsOption[];
23
+ value?: string;
24
+ defaultValue?: string;
25
+ onChange?: (value: string) => void;
26
+ name?: string;
27
+ /** Small inline pills instead of tiles. */
28
+ compact?: boolean;
29
+ }
30
+
31
+ /** `.oc-filter-counts`: choose a view; every tile shows its item count. */
32
+ export const FilterCounts = forwardRef<HTMLFieldSetElement, FilterCountsProps>(function FilterCounts(
33
+ { options, value, defaultValue, onChange, name, compact, className, ...rest },
34
+ ref,
35
+ ) {
36
+ const group = name ?? `oc-filter-${useId().replace(/:/g, "")}`;
37
+ const [current, setCurrent] = useControllable<string>(value, defaultValue ?? options[0]?.value ?? "", onChange);
38
+ return (
39
+ <fieldset ref={ref} className={cx("oc-filter-counts", compact && "oc-filter-counts--compact", className)} role="radiogroup" {...rest}>
40
+ {options.map((o) => (
41
+ <label key={o.value} className={cx("oc-filter-counts-item", o.tone && o.tone !== "primary" && `oc-filter-counts-item--${o.tone}`)}>
42
+ <input type="radio" name={group} value={o.value} disabled={o.disabled} checked={current === o.value} onChange={() => setCurrent(o.value)} />
43
+ <span className="oc-filter-counts-value">{o.count}</span>
44
+ <span className="oc-filter-counts-label">{o.label}</span>
45
+ {o.hint != null && <span className="oc-filter-counts-hint">{o.hint}</span>}
46
+ </label>
47
+ ))}
48
+ </fieldset>
49
+ );
50
+ });
@@ -1,55 +1,55 @@
1
- // File: packages/react/src/components/FlipCard.tsx
2
- // Map: props -> FlipCard (optional checkbox -> inner -> front face + flip label -> back face + flip label)
3
- // Same markup as packages/ui/snippets/flip-card.html. trigger="toggle" uses the CSS checkbox, "hover" the --hover
4
- // modifier, "manual" only the `flipped` prop (.is-flipped).
5
- "use client";
6
-
7
- import { forwardRef, useId } from "react";
8
- import type { HTMLAttributes, ReactNode } from "react";
9
- import { cx } from "../utils";
10
- import { useControllable } from "../hooks";
11
-
12
- export interface FlipCardProps extends HTMLAttributes<HTMLDivElement> {
13
- front: ReactNode;
14
- back: ReactNode;
15
- /** How the card flips. Default "toggle" (click / Space). */
16
- trigger?: "toggle" | "hover" | "manual";
17
- flipped?: boolean;
18
- defaultFlipped?: boolean;
19
- onFlippedChange?: (flipped: boolean) => void;
20
- /** Turn top over bottom instead of left over right. */
21
- vertical?: boolean;
22
- /** Text of the flip labels (toggle mode). */
23
- frontLabel?: ReactNode;
24
- backLabel?: ReactNode;
25
- }
26
-
27
- /** `.oc-flip-card`: front / back card that turns in 3D (cross-fades with reduced motion). */
28
- export const FlipCard = forwardRef<HTMLDivElement, FlipCardProps>(function FlipCard(
29
- { front, back, trigger = "toggle", flipped, defaultFlipped = false, onFlippedChange, vertical, frontLabel = "Show answer", backLabel = "Back", className, ...rest },
30
- ref,
31
- ) {
32
- const id = `oc-flip-${useId().replace(/:/g, "")}`;
33
- const [isFlipped, setFlipped] = useControllable(flipped, defaultFlipped, onFlippedChange);
34
- const toggle = trigger === "toggle";
35
- return (
36
- <div
37
- ref={ref}
38
- className={cx("oc-flip-card", trigger === "hover" && "oc-flip-card--hover", vertical && "oc-flip-card--vertical", !toggle && isFlipped && "is-flipped", className)}
39
- tabIndex={trigger === "hover" ? 0 : undefined}
40
- {...rest}
41
- >
42
- {toggle && <input type="checkbox" className="oc-flip-card__toggle" id={id} checked={isFlipped} onChange={(e) => setFlipped(e.target.checked)} />}
43
- <div className="oc-flip-card__inner">
44
- <div className="oc-flip-card__face oc-flip-card__face--front">
45
- {front}
46
- {toggle && <label className="oc-flip-card__flip" htmlFor={id}>{frontLabel}</label>}
47
- </div>
48
- <div className="oc-flip-card__face oc-flip-card__face--back">
49
- {back}
50
- {toggle && <label className="oc-flip-card__flip" htmlFor={id}>{backLabel}</label>}
51
- </div>
52
- </div>
53
- </div>
54
- );
55
- });
1
+ // File: packages/react/src/components/FlipCard.tsx
2
+ // Map: props -> FlipCard (optional checkbox -> inner -> front face + flip label -> back face + flip label)
3
+ // Same markup as packages/ui/snippets/flip-card.html. trigger="toggle" uses the CSS checkbox, "hover" the --hover
4
+ // modifier, "manual" only the `flipped` prop (.is-flipped).
5
+ "use client";
6
+
7
+ import { forwardRef, useId } from "react";
8
+ import type { HTMLAttributes, ReactNode } from "react";
9
+ import { cx } from "../utils";
10
+ import { useControllable } from "../hooks";
11
+
12
+ export interface FlipCardProps extends HTMLAttributes<HTMLDivElement> {
13
+ front: ReactNode;
14
+ back: ReactNode;
15
+ /** How the card flips. Default "toggle" (click / Space). */
16
+ trigger?: "toggle" | "hover" | "manual";
17
+ flipped?: boolean;
18
+ defaultFlipped?: boolean;
19
+ onFlippedChange?: (flipped: boolean) => void;
20
+ /** Turn top over bottom instead of left over right. */
21
+ vertical?: boolean;
22
+ /** Text of the flip labels (toggle mode). */
23
+ frontLabel?: ReactNode;
24
+ backLabel?: ReactNode;
25
+ }
26
+
27
+ /** `.oc-flip-card`: front / back card that turns in 3D (cross-fades with reduced motion). */
28
+ export const FlipCard = forwardRef<HTMLDivElement, FlipCardProps>(function FlipCard(
29
+ { front, back, trigger = "toggle", flipped, defaultFlipped = false, onFlippedChange, vertical, frontLabel = "Show answer", backLabel = "Back", className, ...rest },
30
+ ref,
31
+ ) {
32
+ const id = `oc-flip-${useId().replace(/:/g, "")}`;
33
+ const [isFlipped, setFlipped] = useControllable(flipped, defaultFlipped, onFlippedChange);
34
+ const toggle = trigger === "toggle";
35
+ return (
36
+ <div
37
+ ref={ref}
38
+ className={cx("oc-flip-card", trigger === "hover" && "oc-flip-card--hover", vertical && "oc-flip-card--vertical", !toggle && isFlipped && "is-flipped", className)}
39
+ tabIndex={trigger === "hover" ? 0 : undefined}
40
+ {...rest}
41
+ >
42
+ {toggle && <input type="checkbox" className="oc-flip-card__toggle" id={id} checked={isFlipped} onChange={(e) => setFlipped(e.target.checked)} />}
43
+ <div className="oc-flip-card__inner">
44
+ <div className="oc-flip-card__face oc-flip-card__face--front">
45
+ {front}
46
+ {toggle && <label className="oc-flip-card__flip" htmlFor={id}>{frontLabel}</label>}
47
+ </div>
48
+ <div className="oc-flip-card__face oc-flip-card__face--back">
49
+ {back}
50
+ {toggle && <label className="oc-flip-card__flip" htmlFor={id}>{backLabel}</label>}
51
+ </div>
52
+ </div>
53
+ </div>
54
+ );
55
+ });
@@ -1,54 +1,54 @@
1
- import { Children, cloneElement, forwardRef, isValidElement, useId } from "react";
2
- import type { HTMLAttributes, ReactElement, ReactNode } from "react";
3
- import { cx } from "../utils";
4
-
5
- export interface FormFieldProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
6
- label?: ReactNode;
7
- description?: ReactNode;
8
- /** Error message (`oc-field-error`); also sets `aria-invalid` on the control. */
9
- error?: ReactNode;
10
- /** Horizontal layout (`oc-field--horizontal`). */
11
- horizontal?: boolean;
12
- /** Id of the control; generated when omitted. */
13
- htmlFor?: string;
14
- /** A single control (Input, Select, Textarea...) gets id / aria-describedby / aria-invalid injected. */
15
- children?: ReactNode;
16
- }
17
-
18
- /** `<div class="oc-field">` with label, control, description and error wired through ids. */
19
- export const FormField = forwardRef<HTMLDivElement, FormFieldProps>(function FormField(
20
- { label, description, error, horizontal, htmlFor, className, children, ...rest },
21
- ref,
22
- ) {
23
- const auto = useId();
24
- const only = Children.count(children) === 1 && isValidElement(children) ? (children as ReactElement<Record<string, unknown>>) : null;
25
- const controlId = htmlFor ?? (only?.props.id as string | undefined) ?? `oc-field-${auto.replace(/:/g, "")}`;
26
- const helpId = description != null ? `${controlId}-help` : undefined;
27
- const errorId = error != null ? `${controlId}-error` : undefined;
28
- const describedBy = [only?.props["aria-describedby"] as string | undefined, helpId, errorId].filter(Boolean).join(" ") || undefined;
29
-
30
- const control = only
31
- ? cloneElement(only, {
32
- id: controlId,
33
- "aria-describedby": describedBy,
34
- ...(error != null ? { "aria-invalid": true } : {}),
35
- })
36
- : children;
37
-
38
- return (
39
- <div ref={ref} className={cx("oc-field", horizontal && "oc-field--horizontal", className)} {...rest}>
40
- {label != null && <label className="oc-field-label" htmlFor={controlId}>{label}</label>}
41
- {control}
42
- {description != null && <p className="oc-field-description" id={helpId}>{description}</p>}
43
- {error != null && <p className="oc-field-error" id={errorId} role="alert">{error}</p>}
44
- </div>
45
- );
46
- });
47
-
48
- /** `<div class="oc-field-group">`: stack (or `row`) of related controls. */
49
- export const FieldGroup = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement> & { row?: boolean }>(function FieldGroup(
50
- { row, className, ...rest },
51
- ref,
52
- ) {
53
- return <div ref={ref} className={cx("oc-field-group", row && "oc-field-group--row", className)} {...rest} />;
54
- });
1
+ import { Children, cloneElement, forwardRef, isValidElement, useId } from "react";
2
+ import type { HTMLAttributes, ReactElement, ReactNode } from "react";
3
+ import { cx } from "../utils";
4
+
5
+ export interface FormFieldProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
6
+ label?: ReactNode;
7
+ description?: ReactNode;
8
+ /** Error message (`oc-field-error`); also sets `aria-invalid` on the control. */
9
+ error?: ReactNode;
10
+ /** Horizontal layout (`oc-field--horizontal`). */
11
+ horizontal?: boolean;
12
+ /** Id of the control; generated when omitted. */
13
+ htmlFor?: string;
14
+ /** A single control (Input, Select, Textarea...) gets id / aria-describedby / aria-invalid injected. */
15
+ children?: ReactNode;
16
+ }
17
+
18
+ /** `<div class="oc-field">` with label, control, description and error wired through ids. */
19
+ export const FormField = forwardRef<HTMLDivElement, FormFieldProps>(function FormField(
20
+ { label, description, error, horizontal, htmlFor, className, children, ...rest },
21
+ ref,
22
+ ) {
23
+ const auto = useId();
24
+ const only = Children.count(children) === 1 && isValidElement(children) ? (children as ReactElement<Record<string, unknown>>) : null;
25
+ const controlId = htmlFor ?? (only?.props.id as string | undefined) ?? `oc-field-${auto.replace(/:/g, "")}`;
26
+ const helpId = description != null ? `${controlId}-help` : undefined;
27
+ const errorId = error != null ? `${controlId}-error` : undefined;
28
+ const describedBy = [only?.props["aria-describedby"] as string | undefined, helpId, errorId].filter(Boolean).join(" ") || undefined;
29
+
30
+ const control = only
31
+ ? cloneElement(only, {
32
+ id: controlId,
33
+ "aria-describedby": describedBy,
34
+ ...(error != null ? { "aria-invalid": true } : {}),
35
+ })
36
+ : children;
37
+
38
+ return (
39
+ <div ref={ref} className={cx("oc-field", horizontal && "oc-field--horizontal", className)} {...rest}>
40
+ {label != null && <label className="oc-field-label" htmlFor={controlId}>{label}</label>}
41
+ {control}
42
+ {description != null && <p className="oc-field-description" id={helpId}>{description}</p>}
43
+ {error != null && <p className="oc-field-error" id={errorId} role="alert">{error}</p>}
44
+ </div>
45
+ );
46
+ });
47
+
48
+ /** `<div class="oc-field-group">`: stack (or `row`) of related controls. */
49
+ export const FieldGroup = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement> & { row?: boolean }>(function FieldGroup(
50
+ { row, className, ...rest },
51
+ ref,
52
+ ) {
53
+ return <div ref={ref} className={cx("oc-field-group", row && "oc-field-group--row", className)} {...rest} />;
54
+ });