@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,42 +1,42 @@
1
- import { forwardRef, useId } from "react";
2
- import type { HTMLAttributes, ReactNode } from "react";
3
- import { attrs, cx } from "../utils";
4
-
5
- export interface SiteHeaderLink {
6
- label: ReactNode;
7
- href: string;
8
- current?: boolean;
9
- }
10
-
11
- export interface BlockSiteHeaderProps extends HTMLAttributes<HTMLElement> {
12
- /** Brand link content, e.g. icon tile + name. */
13
- brand: ReactNode;
14
- brandHref?: string;
15
- links: SiteHeaderLink[];
16
- /** Trailing call to action inside the nav (e.g. a `ButtonLink` with `variant="gradient"`). */
17
- cta?: ReactNode;
18
- /** Accessible name of the mobile menu button. */
19
- menuLabel?: string;
20
- }
21
-
22
- /** `.oc-site-header`: sticky header, mobile menu on a native popover (no JavaScript). */
23
- export const BlockSiteHeader = forwardRef<HTMLElement, BlockSiteHeaderProps>(function BlockSiteHeader(
24
- { brand, brandHref = "/", links, cta, menuLabel = "Menu", className, ...rest },
25
- ref,
26
- ) {
27
- const navId = `oc-site-nav-${useId().replace(/:/g, "")}`;
28
- return (
29
- <header ref={ref} className={cx("oc-site-header", className)} {...rest}>
30
- <div className="oc-container oc-site-header__inner">
31
- <a className="oc-site-header__brand" href={brandHref}>{brand}</a>
32
- <button className="oc-site-header__toggle" type="button" aria-label={menuLabel} {...attrs({ popovertarget: navId })}>&#9776;</button>
33
- <nav className="oc-site-header__nav" id={navId} aria-label="Main" {...attrs({ popover: "" })}>
34
- {links.map((l, i) => (
35
- <a key={i} href={l.href} aria-current={l.current ? "page" : undefined}>{l.label}</a>
36
- ))}
37
- {cta}
38
- </nav>
39
- </div>
40
- </header>
41
- );
42
- });
1
+ import { forwardRef, useId } from "react";
2
+ import type { HTMLAttributes, ReactNode } from "react";
3
+ import { attrs, cx } from "../utils";
4
+
5
+ export interface SiteHeaderLink {
6
+ label: ReactNode;
7
+ href: string;
8
+ current?: boolean;
9
+ }
10
+
11
+ export interface BlockSiteHeaderProps extends HTMLAttributes<HTMLElement> {
12
+ /** Brand link content, e.g. icon tile + name. */
13
+ brand: ReactNode;
14
+ brandHref?: string;
15
+ links: SiteHeaderLink[];
16
+ /** Trailing call to action inside the nav (e.g. a `ButtonLink` with `variant="gradient"`). */
17
+ cta?: ReactNode;
18
+ /** Accessible name of the mobile menu button. */
19
+ menuLabel?: string;
20
+ }
21
+
22
+ /** `.oc-site-header`: sticky header, mobile menu on a native popover (no JavaScript). */
23
+ export const BlockSiteHeader = forwardRef<HTMLElement, BlockSiteHeaderProps>(function BlockSiteHeader(
24
+ { brand, brandHref = "/", links, cta, menuLabel = "Menu", className, ...rest },
25
+ ref,
26
+ ) {
27
+ const navId = `oc-site-nav-${useId().replace(/:/g, "")}`;
28
+ return (
29
+ <header ref={ref} className={cx("oc-site-header", className)} {...rest}>
30
+ <div className="oc-container oc-site-header__inner">
31
+ <a className="oc-site-header__brand" href={brandHref}>{brand}</a>
32
+ <button className="oc-site-header__toggle" type="button" aria-label={menuLabel} {...attrs({ popovertarget: navId })}>&#9776;</button>
33
+ <nav className="oc-site-header__nav" id={navId} aria-label="Main" {...attrs({ popover: "" })}>
34
+ {links.map((l, i) => (
35
+ <a key={i} href={l.href} aria-current={l.current ? "page" : undefined}>{l.label}</a>
36
+ ))}
37
+ {cta}
38
+ </nav>
39
+ </div>
40
+ </header>
41
+ );
42
+ });
@@ -1,58 +1,58 @@
1
- import { forwardRef } from "react";
2
- import type { HTMLAttributes, ReactNode } from "react";
3
- import { cx } from "../utils";
4
-
5
- export interface SpotlightModule {
6
- icon?: ReactNode;
7
- label: ReactNode;
8
- }
9
-
10
- export interface BlockSpotlightProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
11
- /** Accent name (`green`, `violet`...), applied as `oc-accent--{accent}`. */
12
- accent?: string;
13
- /** Pill text (with a status dot). */
14
- badge?: ReactNode;
15
- /** Glyph or SVG in the big icon tile. */
16
- icon?: ReactNode;
17
- title: ReactNode;
18
- tagline?: ReactNode;
19
- lead?: ReactNode;
20
- /** Call to action (e.g. a `ButtonLink` with `variant="gradient" size="lg"`). */
21
- action?: ReactNode;
22
- modules?: SpotlightModule[];
23
- }
24
-
25
- /** `.oc-spotlight`: highlighted flagship product with a module list. */
26
- export const BlockSpotlight = forwardRef<HTMLElement, BlockSpotlightProps>(function BlockSpotlight(
27
- { accent, badge, icon, title, tagline, lead, action, modules, className, ...rest },
28
- ref,
29
- ) {
30
- return (
31
- <section ref={ref} className={cx("oc-section-pad", className)} {...rest}>
32
- <div className="oc-container">
33
- <div className={cx("oc-spotlight", accent && `oc-accent--${accent}`)}>
34
- <div>
35
- {badge != null && <span className="oc-pill oc-pill--accent"><span className="oc-status-dot" />{badge}</span>}
36
- <h2 className="oc-spotlight__title">
37
- {icon != null && <span className="oc-icon-tile oc-icon-tile--lg" aria-hidden="true">{icon}</span>}
38
- {title}
39
- </h2>
40
- {tagline != null && <p className="oc-spotlight__tagline">{tagline}</p>}
41
- {lead != null && <p className="oc-lead">{lead}</p>}
42
- {action}
43
- </div>
44
- {modules && (
45
- <ul className="oc-spotlight__modules">
46
- {modules.map((m, i) => (
47
- <li key={i}>
48
- {m.icon != null && <span className="oc-icon-tile oc-icon-tile--sm" aria-hidden="true">{m.icon}</span>}
49
- {m.label}
50
- </li>
51
- ))}
52
- </ul>
53
- )}
54
- </div>
55
- </div>
56
- </section>
57
- );
58
- });
1
+ import { forwardRef } from "react";
2
+ import type { HTMLAttributes, ReactNode } from "react";
3
+ import { cx } from "../utils";
4
+
5
+ export interface SpotlightModule {
6
+ icon?: ReactNode;
7
+ label: ReactNode;
8
+ }
9
+
10
+ export interface BlockSpotlightProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
11
+ /** Accent name (`green`, `violet`...), applied as `oc-accent--{accent}`. */
12
+ accent?: string;
13
+ /** Pill text (with a status dot). */
14
+ badge?: ReactNode;
15
+ /** Glyph or SVG in the big icon tile. */
16
+ icon?: ReactNode;
17
+ title: ReactNode;
18
+ tagline?: ReactNode;
19
+ lead?: ReactNode;
20
+ /** Call to action (e.g. a `ButtonLink` with `variant="gradient" size="lg"`). */
21
+ action?: ReactNode;
22
+ modules?: SpotlightModule[];
23
+ }
24
+
25
+ /** `.oc-spotlight`: highlighted flagship product with a module list. */
26
+ export const BlockSpotlight = forwardRef<HTMLElement, BlockSpotlightProps>(function BlockSpotlight(
27
+ { accent, badge, icon, title, tagline, lead, action, modules, className, ...rest },
28
+ ref,
29
+ ) {
30
+ return (
31
+ <section ref={ref} className={cx("oc-section-pad", className)} {...rest}>
32
+ <div className="oc-container">
33
+ <div className={cx("oc-spotlight", accent && `oc-accent--${accent}`)}>
34
+ <div>
35
+ {badge != null && <span className="oc-pill oc-pill--accent"><span className="oc-status-dot" />{badge}</span>}
36
+ <h2 className="oc-spotlight__title">
37
+ {icon != null && <span className="oc-icon-tile oc-icon-tile--lg" aria-hidden="true">{icon}</span>}
38
+ {title}
39
+ </h2>
40
+ {tagline != null && <p className="oc-spotlight__tagline">{tagline}</p>}
41
+ {lead != null && <p className="oc-lead">{lead}</p>}
42
+ {action}
43
+ </div>
44
+ {modules && (
45
+ <ul className="oc-spotlight__modules">
46
+ {modules.map((m, i) => (
47
+ <li key={i}>
48
+ {m.icon != null && <span className="oc-icon-tile oc-icon-tile--sm" aria-hidden="true">{m.icon}</span>}
49
+ {m.label}
50
+ </li>
51
+ ))}
52
+ </ul>
53
+ )}
54
+ </div>
55
+ </div>
56
+ </section>
57
+ );
58
+ });
@@ -1,26 +1,26 @@
1
- import { forwardRef } from "react";
2
- import type { HTMLAttributes, ReactNode } from "react";
3
- import { cx } from "../utils";
4
-
5
- export interface Stat {
6
- value: ReactNode;
7
- label: ReactNode;
8
- }
9
-
10
- export interface BlockStatsProps extends HTMLAttributes<HTMLDivElement> {
11
- stats: Stat[];
12
- }
13
-
14
- /** `.oc-block-stats`: key figures row. */
15
- export const BlockStats = forwardRef<HTMLDivElement, BlockStatsProps>(function BlockStats({ stats, className, ...rest }, ref) {
16
- return (
17
- <div ref={ref} className={cx("oc-block-stats", className)} {...rest}>
18
- {stats.map((s, i) => (
19
- <div className="oc-block-stat" key={i}>
20
- <div className="oc-block-stat-value">{s.value}</div>
21
- <div className="oc-block-stat-label">{s.label}</div>
22
- </div>
23
- ))}
24
- </div>
25
- );
26
- });
1
+ import { forwardRef } from "react";
2
+ import type { HTMLAttributes, ReactNode } from "react";
3
+ import { cx } from "../utils";
4
+
5
+ export interface Stat {
6
+ value: ReactNode;
7
+ label: ReactNode;
8
+ }
9
+
10
+ export interface BlockStatsProps extends HTMLAttributes<HTMLDivElement> {
11
+ stats: Stat[];
12
+ }
13
+
14
+ /** `.oc-block-stats`: key figures row. */
15
+ export const BlockStats = forwardRef<HTMLDivElement, BlockStatsProps>(function BlockStats({ stats, className, ...rest }, ref) {
16
+ return (
17
+ <div ref={ref} className={cx("oc-block-stats", className)} {...rest}>
18
+ {stats.map((s, i) => (
19
+ <div className="oc-block-stat" key={i}>
20
+ <div className="oc-block-stat-value">{s.value}</div>
21
+ <div className="oc-block-stat-label">{s.label}</div>
22
+ </div>
23
+ ))}
24
+ </div>
25
+ );
26
+ });
@@ -1,31 +1,31 @@
1
- import { forwardRef } from "react";
2
- import type { HTMLAttributes, ReactNode } from "react";
3
- import { cx } from "../utils";
4
- import { Counter } from "../components/Counter";
5
-
6
- export interface CounterStat {
7
- to: number;
8
- suffix?: string;
9
- label: ReactNode;
10
- }
11
-
12
- export interface BlockStatsCounterProps extends HTMLAttributes<HTMLDivElement> {
13
- stats: CounterStat[];
14
- }
15
-
16
- /** `.oc-block-stats` whose values count up when scrolled into view (CSS only). */
17
- export const BlockStatsCounter = forwardRef<HTMLDivElement, BlockStatsCounterProps>(function BlockStatsCounter(
18
- { stats, className, ...rest },
19
- ref,
20
- ) {
21
- return (
22
- <div ref={ref} className={cx("oc-block-stats", className)} {...rest}>
23
- {stats.map((s, i) => (
24
- <div className="oc-block-stat" key={i}>
25
- <Counter className="oc-block-stat-value" to={s.to} suffix={s.suffix} />
26
- <span className="oc-block-stat-label">{s.label}</span>
27
- </div>
28
- ))}
29
- </div>
30
- );
31
- });
1
+ import { forwardRef } from "react";
2
+ import type { HTMLAttributes, ReactNode } from "react";
3
+ import { cx } from "../utils";
4
+ import { Counter } from "../components/Counter";
5
+
6
+ export interface CounterStat {
7
+ to: number;
8
+ suffix?: string;
9
+ label: ReactNode;
10
+ }
11
+
12
+ export interface BlockStatsCounterProps extends HTMLAttributes<HTMLDivElement> {
13
+ stats: CounterStat[];
14
+ }
15
+
16
+ /** `.oc-block-stats` whose values count up when scrolled into view (CSS only). */
17
+ export const BlockStatsCounter = forwardRef<HTMLDivElement, BlockStatsCounterProps>(function BlockStatsCounter(
18
+ { stats, className, ...rest },
19
+ ref,
20
+ ) {
21
+ return (
22
+ <div ref={ref} className={cx("oc-block-stats", className)} {...rest}>
23
+ {stats.map((s, i) => (
24
+ <div className="oc-block-stat" key={i}>
25
+ <Counter className="oc-block-stat-value" to={s.to} suffix={s.suffix} />
26
+ <span className="oc-block-stat-label">{s.label}</span>
27
+ </div>
28
+ ))}
29
+ </div>
30
+ );
31
+ });
@@ -1,48 +1,48 @@
1
- import { forwardRef } from "react";
2
- import type { HTMLAttributes, ReactNode } from "react";
3
- import { cx } from "../utils";
4
- import { Badge } from "../components/Badge";
5
- import type { BadgeVariant } from "../components/Badge";
6
- import { Dropdown } from "../components/Dropdown";
7
- import { User } from "../components/User";
8
-
9
- export interface TeamMember {
10
- name: ReactNode;
11
- email?: ReactNode;
12
- initials?: string;
13
- avatarSrc?: string;
14
- /** Role / status badge text. */
15
- role?: ReactNode;
16
- roleVariant?: BadgeVariant;
17
- /** Row action menu content (`DropdownItem`s). Adds a "..." trigger when set. */
18
- menu?: ReactNode;
19
- menuLabel?: string;
20
- }
21
-
22
- export interface BlockTeamListProps extends HTMLAttributes<HTMLDivElement> {
23
- members: TeamMember[];
24
- }
25
-
26
- /** `.oc-block-team`: member rows with avatar, role badge and a row action menu. */
27
- export const BlockTeamList = forwardRef<HTMLDivElement, BlockTeamListProps>(function BlockTeamList(
28
- { members, className, ...rest },
29
- ref,
30
- ) {
31
- return (
32
- <div ref={ref} className={cx("oc-block-team", className)} {...rest}>
33
- {members.map((m, i) => (
34
- <div className="oc-block-team-row" key={i}>
35
- <User name={m.name} email={m.email} initials={m.initials} avatarSrc={m.avatarSrc} />
36
- <div className="oc-block-team-meta">
37
- {m.role != null && <Badge variant={m.roleVariant}>{m.role}</Badge>}
38
- {m.menu != null && (
39
- <Dropdown trigger={<>&hellip;</>} triggerProps={{ variant: "ghost", icon: true, size: "sm", type: "button", "aria-label": m.menuLabel ?? "Member actions" }}>
40
- {m.menu}
41
- </Dropdown>
42
- )}
43
- </div>
44
- </div>
45
- ))}
46
- </div>
47
- );
48
- });
1
+ import { forwardRef } from "react";
2
+ import type { HTMLAttributes, ReactNode } from "react";
3
+ import { cx } from "../utils";
4
+ import { Badge } from "../components/Badge";
5
+ import type { BadgeVariant } from "../components/Badge";
6
+ import { Dropdown } from "../components/Dropdown";
7
+ import { User } from "../components/User";
8
+
9
+ export interface TeamMember {
10
+ name: ReactNode;
11
+ email?: ReactNode;
12
+ initials?: string;
13
+ avatarSrc?: string;
14
+ /** Role / status badge text. */
15
+ role?: ReactNode;
16
+ roleVariant?: BadgeVariant;
17
+ /** Row action menu content (`DropdownItem`s). Adds a "..." trigger when set. */
18
+ menu?: ReactNode;
19
+ menuLabel?: string;
20
+ }
21
+
22
+ export interface BlockTeamListProps extends HTMLAttributes<HTMLDivElement> {
23
+ members: TeamMember[];
24
+ }
25
+
26
+ /** `.oc-block-team`: member rows with avatar, role badge and a row action menu. */
27
+ export const BlockTeamList = forwardRef<HTMLDivElement, BlockTeamListProps>(function BlockTeamList(
28
+ { members, className, ...rest },
29
+ ref,
30
+ ) {
31
+ return (
32
+ <div ref={ref} className={cx("oc-block-team", className)} {...rest}>
33
+ {members.map((m, i) => (
34
+ <div className="oc-block-team-row" key={i}>
35
+ <User name={m.name} email={m.email} initials={m.initials} avatarSrc={m.avatarSrc} />
36
+ <div className="oc-block-team-meta">
37
+ {m.role != null && <Badge variant={m.roleVariant}>{m.role}</Badge>}
38
+ {m.menu != null && (
39
+ <Dropdown trigger={<>&hellip;</>} triggerProps={{ variant: "ghost", icon: true, size: "sm", type: "button", "aria-label": m.menuLabel ?? "Member actions" }}>
40
+ {m.menu}
41
+ </Dropdown>
42
+ )}
43
+ </div>
44
+ </div>
45
+ ))}
46
+ </div>
47
+ );
48
+ });
@@ -1,57 +1,57 @@
1
- "use client";
2
-
3
- import { forwardRef, useId, useState } from "react";
4
- import type { FormEvent, HTMLAttributes, ReactNode } from "react";
5
- import { cx } from "../utils";
6
- import { CopyField } from "../components/CopyField";
7
- import { Heading } from "../components/Heading";
8
- import { OtpInput } from "../components/OtpInput";
9
- import { RecoveryCodes } from "../components/RecoveryCodes";
10
-
11
- export interface BlockTwoFactorProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSubmit"> {
12
- /** QR code image / svg; a labelled placeholder box is rendered when omitted. */
13
- qr?: ReactNode;
14
- setupKey: string;
15
- recoveryCodes?: string[];
16
- codeLength?: number;
17
- /** Called with the entered code when the form is submitted. */
18
- onSubmit?: (code: string) => void;
19
- submitLabel?: ReactNode;
20
- }
21
-
22
- /** `.oc-block-twofactor`: authenticator setup with QR, copyable key, OTP input and recovery codes. */
23
- export const BlockTwoFactor = forwardRef<HTMLDivElement, BlockTwoFactorProps>(function BlockTwoFactor(
24
- { qr, setupKey, recoveryCodes, codeLength = 6, onSubmit, submitLabel = "Enable two-factor", className, ...rest },
25
- ref,
26
- ) {
27
- const id = `oc-twofactor-${useId().replace(/:/g, "")}`;
28
- const [code, setCode] = useState("");
29
- const submit = (e: FormEvent) => {
30
- e.preventDefault();
31
- onSubmit?.(code);
32
- };
33
- return (
34
- <div ref={ref} className={cx("oc-block-twofactor", className)} {...rest}>
35
- <section className="oc-block-twofactor-step">
36
- <Heading size="sm" title="1. Scan the QR code" description="Use an authenticator app, or enter the key manually." />
37
- <div className="oc-block-twofactor-center">
38
- {qr ?? <div className="oc-block-twofactor-qr" role="img" aria-label="QR code">QR code</div>}
39
- <CopyField value={setupKey} label="Setup key" />
40
- </div>
41
- </section>
42
- <form className="oc-block-twofactor-step" onSubmit={submit}>
43
- <Heading size="sm" title="2. Confirm with a code" />
44
- <div className="oc-field">
45
- <label className="oc-field-label" htmlFor={id}>Authentication code</label>
46
- <OtpInput id={id} name="code" length={codeLength} value={code} onChange={setCode} />
47
- </div>
48
- <button className="oc-btn" type="submit">{submitLabel}</button>
49
- </form>
50
- {recoveryCodes && (
51
- <section className="oc-block-twofactor-step">
52
- <RecoveryCodes codes={recoveryCodes} />
53
- </section>
54
- )}
55
- </div>
56
- );
57
- });
1
+ "use client";
2
+
3
+ import { forwardRef, useId, useState } from "react";
4
+ import type { FormEvent, HTMLAttributes, ReactNode } from "react";
5
+ import { cx } from "../utils";
6
+ import { CopyField } from "../components/CopyField";
7
+ import { Heading } from "../components/Heading";
8
+ import { OtpInput } from "../components/OtpInput";
9
+ import { RecoveryCodes } from "../components/RecoveryCodes";
10
+
11
+ export interface BlockTwoFactorProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSubmit"> {
12
+ /** QR code image / svg; a labelled placeholder box is rendered when omitted. */
13
+ qr?: ReactNode;
14
+ setupKey: string;
15
+ recoveryCodes?: string[];
16
+ codeLength?: number;
17
+ /** Called with the entered code when the form is submitted. */
18
+ onSubmit?: (code: string) => void;
19
+ submitLabel?: ReactNode;
20
+ }
21
+
22
+ /** `.oc-block-twofactor`: authenticator setup with QR, copyable key, OTP input and recovery codes. */
23
+ export const BlockTwoFactor = forwardRef<HTMLDivElement, BlockTwoFactorProps>(function BlockTwoFactor(
24
+ { qr, setupKey, recoveryCodes, codeLength = 6, onSubmit, submitLabel = "Enable two-factor", className, ...rest },
25
+ ref,
26
+ ) {
27
+ const id = `oc-twofactor-${useId().replace(/:/g, "")}`;
28
+ const [code, setCode] = useState("");
29
+ const submit = (e: FormEvent) => {
30
+ e.preventDefault();
31
+ onSubmit?.(code);
32
+ };
33
+ return (
34
+ <div ref={ref} className={cx("oc-block-twofactor", className)} {...rest}>
35
+ <section className="oc-block-twofactor-step">
36
+ <Heading size="sm" title="1. Scan the QR code" description="Use an authenticator app, or enter the key manually." />
37
+ <div className="oc-block-twofactor-center">
38
+ {qr ?? <div className="oc-block-twofactor-qr" role="img" aria-label="QR code">QR code</div>}
39
+ <CopyField value={setupKey} label="Setup key" />
40
+ </div>
41
+ </section>
42
+ <form className="oc-block-twofactor-step" onSubmit={submit}>
43
+ <Heading size="sm" title="2. Confirm with a code" />
44
+ <div className="oc-field">
45
+ <label className="oc-field-label" htmlFor={id}>Authentication code</label>
46
+ <OtpInput id={id} name="code" length={codeLength} value={code} onChange={setCode} />
47
+ </div>
48
+ <button className="oc-btn" type="submit">{submitLabel}</button>
49
+ </form>
50
+ {recoveryCodes && (
51
+ <section className="oc-block-twofactor-step">
52
+ <RecoveryCodes codes={recoveryCodes} />
53
+ </section>
54
+ )}
55
+ </div>
56
+ );
57
+ });