@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,101 +1,101 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { forwardRef, useId, useRef } from "react";
|
|
4
|
-
import type { DialogHTMLAttributes, ReactNode } from "react";
|
|
5
|
-
import { attrs, cx } from "../utils";
|
|
6
|
-
import { DialogContext, useControllable, useDialog, useMergedRef } from "../hooks";
|
|
7
|
-
import type { DialogState } from "../hooks";
|
|
8
|
-
import { Stepper } from "../components/Stepper";
|
|
9
|
-
|
|
10
|
-
export interface WizardStep {
|
|
11
|
-
label: ReactNode;
|
|
12
|
-
/** Step content (only the active step is rendered). */
|
|
13
|
-
content: ReactNode;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface BlockWizardDialogProps extends Omit<DialogHTMLAttributes<HTMLDialogElement>, "open" | "onClose" | "title">, DialogState {
|
|
17
|
-
steps: WizardStep[];
|
|
18
|
-
/** Active step (0-based, controlled). */
|
|
19
|
-
step?: number;
|
|
20
|
-
defaultStep?: number;
|
|
21
|
-
onStepChange?: (step: number) => void;
|
|
22
|
-
/** Called when "Finish" is pressed on the last step. */
|
|
23
|
-
onFinish?: () => void;
|
|
24
|
-
backLabel?: ReactNode;
|
|
25
|
-
nextLabel?: ReactNode;
|
|
26
|
-
finishLabel?: ReactNode;
|
|
27
|
-
/** Text under the title; defaults to `Step {n} of {total}`. */
|
|
28
|
-
description?: ReactNode;
|
|
29
|
-
stepperLabel?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** `dialog.oc-modal` wizard: stepper rail on the left, step content and Back / Next footer. */
|
|
33
|
-
export const BlockWizardDialog = forwardRef<HTMLDialogElement, BlockWizardDialogProps>(function BlockWizardDialog(
|
|
34
|
-
{
|
|
35
|
-
open,
|
|
36
|
-
defaultOpen,
|
|
37
|
-
onOpenChange,
|
|
38
|
-
steps,
|
|
39
|
-
step,
|
|
40
|
-
defaultStep = 0,
|
|
41
|
-
onStepChange,
|
|
42
|
-
onFinish,
|
|
43
|
-
backLabel = "Back",
|
|
44
|
-
nextLabel = "Next",
|
|
45
|
-
finishLabel = "Finish",
|
|
46
|
-
description,
|
|
47
|
-
stepperLabel = "Setup steps",
|
|
48
|
-
className,
|
|
49
|
-
...rest
|
|
50
|
-
},
|
|
51
|
-
forwardedRef,
|
|
52
|
-
) {
|
|
53
|
-
const inner = useRef<HTMLDialogElement>(null);
|
|
54
|
-
const ref = useMergedRef(inner, forwardedRef);
|
|
55
|
-
const titleId = `oc-wizard-${useId().replace(/:/g, "")}-title`;
|
|
56
|
-
const dlg = useDialog(inner, { open, defaultOpen, onOpenChange });
|
|
57
|
-
const [current, setCurrent] = useControllable<number>(step, defaultStep, onStepChange);
|
|
58
|
-
const last = current >= steps.length - 1;
|
|
59
|
-
const active = steps[current];
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<dialog
|
|
63
|
-
ref={ref}
|
|
64
|
-
className={cx("oc-modal", className)}
|
|
65
|
-
aria-labelledby={titleId}
|
|
66
|
-
{...attrs({ closedby: "closerequest" })}
|
|
67
|
-
{...rest}
|
|
68
|
-
onClose={dlg.onClose}
|
|
69
|
-
>
|
|
70
|
-
<DialogContext.Provider value={{ close: dlg.close }}>
|
|
71
|
-
<div className="oc-modal-content oc-modal-content--xl">
|
|
72
|
-
<div className="oc-block-wizard">
|
|
73
|
-
<div className="oc-block-wizard-rail">
|
|
74
|
-
<Stepper steps={steps.map((s) => ({ label: s.label }))} current={current} onStepChange={setCurrent} aria-label={stepperLabel} />
|
|
75
|
-
</div>
|
|
76
|
-
<div className="oc-block-wizard-main">
|
|
77
|
-
<div className="oc-modal-header">
|
|
78
|
-
<h2 className="oc-modal-title" id={titleId}>{active?.label}</h2>
|
|
79
|
-
<p className="oc-modal-description">{description ?? `Step ${current + 1} of ${steps.length}`}</p>
|
|
80
|
-
</div>
|
|
81
|
-
<div className="oc-block-wizard-step">{active?.content}</div>
|
|
82
|
-
<div className="oc-modal-footer">
|
|
83
|
-
<button className="oc-btn oc-btn--outline" type="button" disabled={current === 0} onClick={() => setCurrent(current - 1)}>{backLabel}</button>
|
|
84
|
-
<button
|
|
85
|
-
className="oc-btn"
|
|
86
|
-
type="button"
|
|
87
|
-
onClick={() => {
|
|
88
|
-
if (last) onFinish?.();
|
|
89
|
-
else setCurrent(current + 1);
|
|
90
|
-
}}
|
|
91
|
-
>
|
|
92
|
-
{last ? finishLabel : nextLabel}
|
|
93
|
-
</button>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
</DialogContext.Provider>
|
|
99
|
-
</dialog>
|
|
100
|
-
);
|
|
101
|
-
});
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { forwardRef, useId, useRef } from "react";
|
|
4
|
+
import type { DialogHTMLAttributes, ReactNode } from "react";
|
|
5
|
+
import { attrs, cx } from "../utils";
|
|
6
|
+
import { DialogContext, useControllable, useDialog, useMergedRef } from "../hooks";
|
|
7
|
+
import type { DialogState } from "../hooks";
|
|
8
|
+
import { Stepper } from "../components/Stepper";
|
|
9
|
+
|
|
10
|
+
export interface WizardStep {
|
|
11
|
+
label: ReactNode;
|
|
12
|
+
/** Step content (only the active step is rendered). */
|
|
13
|
+
content: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface BlockWizardDialogProps extends Omit<DialogHTMLAttributes<HTMLDialogElement>, "open" | "onClose" | "title">, DialogState {
|
|
17
|
+
steps: WizardStep[];
|
|
18
|
+
/** Active step (0-based, controlled). */
|
|
19
|
+
step?: number;
|
|
20
|
+
defaultStep?: number;
|
|
21
|
+
onStepChange?: (step: number) => void;
|
|
22
|
+
/** Called when "Finish" is pressed on the last step. */
|
|
23
|
+
onFinish?: () => void;
|
|
24
|
+
backLabel?: ReactNode;
|
|
25
|
+
nextLabel?: ReactNode;
|
|
26
|
+
finishLabel?: ReactNode;
|
|
27
|
+
/** Text under the title; defaults to `Step {n} of {total}`. */
|
|
28
|
+
description?: ReactNode;
|
|
29
|
+
stepperLabel?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** `dialog.oc-modal` wizard: stepper rail on the left, step content and Back / Next footer. */
|
|
33
|
+
export const BlockWizardDialog = forwardRef<HTMLDialogElement, BlockWizardDialogProps>(function BlockWizardDialog(
|
|
34
|
+
{
|
|
35
|
+
open,
|
|
36
|
+
defaultOpen,
|
|
37
|
+
onOpenChange,
|
|
38
|
+
steps,
|
|
39
|
+
step,
|
|
40
|
+
defaultStep = 0,
|
|
41
|
+
onStepChange,
|
|
42
|
+
onFinish,
|
|
43
|
+
backLabel = "Back",
|
|
44
|
+
nextLabel = "Next",
|
|
45
|
+
finishLabel = "Finish",
|
|
46
|
+
description,
|
|
47
|
+
stepperLabel = "Setup steps",
|
|
48
|
+
className,
|
|
49
|
+
...rest
|
|
50
|
+
},
|
|
51
|
+
forwardedRef,
|
|
52
|
+
) {
|
|
53
|
+
const inner = useRef<HTMLDialogElement>(null);
|
|
54
|
+
const ref = useMergedRef(inner, forwardedRef);
|
|
55
|
+
const titleId = `oc-wizard-${useId().replace(/:/g, "")}-title`;
|
|
56
|
+
const dlg = useDialog(inner, { open, defaultOpen, onOpenChange });
|
|
57
|
+
const [current, setCurrent] = useControllable<number>(step, defaultStep, onStepChange);
|
|
58
|
+
const last = current >= steps.length - 1;
|
|
59
|
+
const active = steps[current];
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<dialog
|
|
63
|
+
ref={ref}
|
|
64
|
+
className={cx("oc-modal", className)}
|
|
65
|
+
aria-labelledby={titleId}
|
|
66
|
+
{...attrs({ closedby: "closerequest" })}
|
|
67
|
+
{...rest}
|
|
68
|
+
onClose={dlg.onClose}
|
|
69
|
+
>
|
|
70
|
+
<DialogContext.Provider value={{ close: dlg.close }}>
|
|
71
|
+
<div className="oc-modal-content oc-modal-content--xl">
|
|
72
|
+
<div className="oc-block-wizard">
|
|
73
|
+
<div className="oc-block-wizard-rail">
|
|
74
|
+
<Stepper steps={steps.map((s) => ({ label: s.label }))} current={current} onStepChange={setCurrent} aria-label={stepperLabel} />
|
|
75
|
+
</div>
|
|
76
|
+
<div className="oc-block-wizard-main">
|
|
77
|
+
<div className="oc-modal-header">
|
|
78
|
+
<h2 className="oc-modal-title" id={titleId}>{active?.label}</h2>
|
|
79
|
+
<p className="oc-modal-description">{description ?? `Step ${current + 1} of ${steps.length}`}</p>
|
|
80
|
+
</div>
|
|
81
|
+
<div className="oc-block-wizard-step">{active?.content}</div>
|
|
82
|
+
<div className="oc-modal-footer">
|
|
83
|
+
<button className="oc-btn oc-btn--outline" type="button" disabled={current === 0} onClick={() => setCurrent(current - 1)}>{backLabel}</button>
|
|
84
|
+
<button
|
|
85
|
+
className="oc-btn"
|
|
86
|
+
type="button"
|
|
87
|
+
onClick={() => {
|
|
88
|
+
if (last) onFinish?.();
|
|
89
|
+
else setCurrent(current + 1);
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
{last ? finishLabel : nextLabel}
|
|
93
|
+
</button>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</DialogContext.Provider>
|
|
99
|
+
</dialog>
|
|
100
|
+
);
|
|
101
|
+
});
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { forwardRef } from "react";
|
|
2
|
-
import type { DetailsHTMLAttributes, HTMLAttributes, ReactNode } from "react";
|
|
3
|
-
import { cx } from "../utils";
|
|
4
|
-
|
|
5
|
-
/** `<div class="oc-accordion">` */
|
|
6
|
-
export const Accordion = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(function Accordion(
|
|
7
|
-
{ className, ...rest },
|
|
8
|
-
ref,
|
|
9
|
-
) {
|
|
10
|
-
return <div ref={ref} className={cx("oc-accordion", className)} {...rest} />;
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
export interface AccordionItemProps extends Omit<DetailsHTMLAttributes<HTMLDetailsElement>, "title"> {
|
|
14
|
-
/** Trigger text (`summary.oc-accordion-trigger`). */
|
|
15
|
-
title: ReactNode;
|
|
16
|
-
/** Items with the same `name` open one at a time (native exclusive details). */
|
|
17
|
-
name?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** `<details class="oc-accordion-item" name>` */
|
|
21
|
-
export const AccordionItem = forwardRef<HTMLDetailsElement, AccordionItemProps>(function AccordionItem(
|
|
22
|
-
{ title, className, children, ...rest },
|
|
23
|
-
ref,
|
|
24
|
-
) {
|
|
25
|
-
return (
|
|
26
|
-
<details ref={ref} className={cx("oc-accordion-item", className)} {...(rest as object)}>
|
|
27
|
-
<summary className="oc-accordion-trigger">{title}</summary>
|
|
28
|
-
<div className="oc-accordion-content">{children}</div>
|
|
29
|
-
</details>
|
|
30
|
-
);
|
|
31
|
-
});
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
import type { DetailsHTMLAttributes, HTMLAttributes, ReactNode } from "react";
|
|
3
|
+
import { cx } from "../utils";
|
|
4
|
+
|
|
5
|
+
/** `<div class="oc-accordion">` */
|
|
6
|
+
export const Accordion = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(function Accordion(
|
|
7
|
+
{ className, ...rest },
|
|
8
|
+
ref,
|
|
9
|
+
) {
|
|
10
|
+
return <div ref={ref} className={cx("oc-accordion", className)} {...rest} />;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export interface AccordionItemProps extends Omit<DetailsHTMLAttributes<HTMLDetailsElement>, "title"> {
|
|
14
|
+
/** Trigger text (`summary.oc-accordion-trigger`). */
|
|
15
|
+
title: ReactNode;
|
|
16
|
+
/** Items with the same `name` open one at a time (native exclusive details). */
|
|
17
|
+
name?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** `<details class="oc-accordion-item" name>` */
|
|
21
|
+
export const AccordionItem = forwardRef<HTMLDetailsElement, AccordionItemProps>(function AccordionItem(
|
|
22
|
+
{ title, className, children, ...rest },
|
|
23
|
+
ref,
|
|
24
|
+
) {
|
|
25
|
+
return (
|
|
26
|
+
<details ref={ref} className={cx("oc-accordion-item", className)} {...(rest as object)}>
|
|
27
|
+
<summary className="oc-accordion-trigger">{title}</summary>
|
|
28
|
+
<div className="oc-accordion-content">{children}</div>
|
|
29
|
+
</details>
|
|
30
|
+
);
|
|
31
|
+
});
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
// File: packages/react/src/components/AccordionAccent.tsx
|
|
2
|
-
// Map: AccordionAccent = div.oc-accordion.oc-accordion--accent (+ color) -> AccordionItem children
|
|
3
|
-
// Use the existing <AccordionItem> inside. Color an item with className "oc-accent--violet"
|
|
4
|
-
// or "oc-accordion-item--warning". AccordionAccentMeta is the small label at the end of a title.
|
|
5
|
-
import { forwardRef } from "react";
|
|
6
|
-
import type { HTMLAttributes } from "react";
|
|
7
|
-
import { cx } from "../utils";
|
|
8
|
-
|
|
9
|
-
export type AccordionAccentPreset = "blue" | "cyan" | "green" | "orange" | "red" | "violet" | "yellow";
|
|
10
|
-
export type AccordionAccentTone = "success" | "warning" | "danger";
|
|
11
|
-
|
|
12
|
-
export interface AccordionAccentProps extends HTMLAttributes<HTMLDivElement> {
|
|
13
|
-
/** Color preset for all items (an `.oc-accent--*` class). */
|
|
14
|
-
accent?: AccordionAccentPreset;
|
|
15
|
-
/** Semantic color for all items. */
|
|
16
|
-
tone?: AccordionAccentTone;
|
|
17
|
-
/** Only the accent edge, no card frame. */
|
|
18
|
-
flush?: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** `.oc-accordion--accent`: accordion with separate cards and a colored start edge. */
|
|
22
|
-
export const AccordionAccent = forwardRef<HTMLDivElement, AccordionAccentProps>(function AccordionAccent(
|
|
23
|
-
{ accent, tone, flush, className, ...rest },
|
|
24
|
-
ref,
|
|
25
|
-
) {
|
|
26
|
-
const cls = cx(
|
|
27
|
-
"oc-accordion oc-accordion--accent",
|
|
28
|
-
flush && "oc-accordion--flush",
|
|
29
|
-
accent && `oc-accent--${accent}`,
|
|
30
|
-
tone && `oc-accordion--${tone}`,
|
|
31
|
-
className,
|
|
32
|
-
);
|
|
33
|
-
return <div ref={ref} className={cls} {...rest} />;
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
/** `.oc-accordion-accent-meta`: short accent-colored label inside an item title. */
|
|
37
|
-
export function AccordionAccentMeta({ className, ...rest }: HTMLAttributes<HTMLSpanElement>) {
|
|
38
|
-
return <span className={cx("oc-accordion-accent-meta", className)} {...rest} />;
|
|
39
|
-
}
|
|
1
|
+
// File: packages/react/src/components/AccordionAccent.tsx
|
|
2
|
+
// Map: AccordionAccent = div.oc-accordion.oc-accordion--accent (+ color) -> AccordionItem children
|
|
3
|
+
// Use the existing <AccordionItem> inside. Color an item with className "oc-accent--violet"
|
|
4
|
+
// or "oc-accordion-item--warning". AccordionAccentMeta is the small label at the end of a title.
|
|
5
|
+
import { forwardRef } from "react";
|
|
6
|
+
import type { HTMLAttributes } from "react";
|
|
7
|
+
import { cx } from "../utils";
|
|
8
|
+
|
|
9
|
+
export type AccordionAccentPreset = "blue" | "cyan" | "green" | "orange" | "red" | "violet" | "yellow";
|
|
10
|
+
export type AccordionAccentTone = "success" | "warning" | "danger";
|
|
11
|
+
|
|
12
|
+
export interface AccordionAccentProps extends HTMLAttributes<HTMLDivElement> {
|
|
13
|
+
/** Color preset for all items (an `.oc-accent--*` class). */
|
|
14
|
+
accent?: AccordionAccentPreset;
|
|
15
|
+
/** Semantic color for all items. */
|
|
16
|
+
tone?: AccordionAccentTone;
|
|
17
|
+
/** Only the accent edge, no card frame. */
|
|
18
|
+
flush?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** `.oc-accordion--accent`: accordion with separate cards and a colored start edge. */
|
|
22
|
+
export const AccordionAccent = forwardRef<HTMLDivElement, AccordionAccentProps>(function AccordionAccent(
|
|
23
|
+
{ accent, tone, flush, className, ...rest },
|
|
24
|
+
ref,
|
|
25
|
+
) {
|
|
26
|
+
const cls = cx(
|
|
27
|
+
"oc-accordion oc-accordion--accent",
|
|
28
|
+
flush && "oc-accordion--flush",
|
|
29
|
+
accent && `oc-accent--${accent}`,
|
|
30
|
+
tone && `oc-accordion--${tone}`,
|
|
31
|
+
className,
|
|
32
|
+
);
|
|
33
|
+
return <div ref={ref} className={cls} {...rest} />;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/** `.oc-accordion-accent-meta`: short accent-colored label inside an item title. */
|
|
37
|
+
export function AccordionAccentMeta({ className, ...rest }: HTMLAttributes<HTMLSpanElement>) {
|
|
38
|
+
return <span className={cx("oc-accordion-accent-meta", className)} {...rest} />;
|
|
39
|
+
}
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
// File: packages/react/src/components/AffixInput.tsx
|
|
2
|
-
// Map: variant presets (currency / percent / email) -> component (input group --affix with inline symbol)
|
|
3
|
-
// Same markup as packages/ui/snippets/affix-input.html. No state: a plain input with a symbol inside the frame.
|
|
4
|
-
import { forwardRef } from "react";
|
|
5
|
-
import type { InputHTMLAttributes, ReactNode } from "react";
|
|
6
|
-
import { cx } from "../utils";
|
|
7
|
-
|
|
8
|
-
export type AffixInputVariant = "currency" | "percent" | "email";
|
|
9
|
-
|
|
10
|
-
export interface AffixInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "prefix"> {
|
|
11
|
-
/** Preset: symbol, input type, inputmode and alignment. */
|
|
12
|
-
variant?: AffixInputVariant;
|
|
13
|
-
/** Symbol before the value (overrides the preset). */
|
|
14
|
-
prefix?: ReactNode;
|
|
15
|
-
/** Symbol after the value (overrides the preset). */
|
|
16
|
-
suffix?: ReactNode;
|
|
17
|
-
/** Right-aligned tabular digits. Off by default: the value sits next to the symbol column. */
|
|
18
|
-
alignEnd?: boolean;
|
|
19
|
-
error?: boolean;
|
|
20
|
-
/** Class for the wrapper (`className` goes to the input). */
|
|
21
|
-
groupClassName?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Preset per variant. Email uses type="email" so :user-invalid checks the address.
|
|
25
|
-
const PRESETS: Record<AffixInputVariant, { prefix?: string; suffix?: string; end: boolean; input: InputHTMLAttributes<HTMLInputElement> }> = {
|
|
26
|
-
currency: { prefix: "\u20AC", end: false, input: { type: "text", inputMode: "decimal" } },
|
|
27
|
-
percent: { prefix: "%", end: false, input: { type: "text", inputMode: "decimal" } },
|
|
28
|
-
email: { prefix: "@", end: false, input: { type: "email", autoComplete: "email" } },
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/** `.oc-input-group.oc-input-group--affix`: symbol (€, %, @) inside the field. */
|
|
32
|
-
export const AffixInput = forwardRef<HTMLInputElement, AffixInputProps>(function AffixInput(
|
|
33
|
-
{ variant, prefix, suffix, alignEnd, error, groupClassName, className, ...rest },
|
|
34
|
-
ref,
|
|
35
|
-
) {
|
|
36
|
-
const preset = variant ? PRESETS[variant] : undefined;
|
|
37
|
-
const before = prefix ?? preset?.prefix;
|
|
38
|
-
const after = suffix ?? preset?.suffix;
|
|
39
|
-
const end = alignEnd ?? preset?.end ?? false;
|
|
40
|
-
return (
|
|
41
|
-
<div className={cx("oc-input-group", "oc-input-group--affix", end && "oc-input-group--end", error && "oc-input-group--error", groupClassName)}>
|
|
42
|
-
{before != null && <span className="oc-input-addon" aria-hidden="true">{before}</span>}
|
|
43
|
-
<input ref={ref} className={cx("oc-input", className)} aria-invalid={error ? true : undefined} {...preset?.input} {...rest} />
|
|
44
|
-
{after != null && <span className="oc-input-addon" aria-hidden="true">{after}</span>}
|
|
45
|
-
</div>
|
|
46
|
-
);
|
|
47
|
-
});
|
|
1
|
+
// File: packages/react/src/components/AffixInput.tsx
|
|
2
|
+
// Map: variant presets (currency / percent / email) -> component (input group --affix with inline symbol)
|
|
3
|
+
// Same markup as packages/ui/snippets/affix-input.html. No state: a plain input with a symbol inside the frame.
|
|
4
|
+
import { forwardRef } from "react";
|
|
5
|
+
import type { InputHTMLAttributes, ReactNode } from "react";
|
|
6
|
+
import { cx } from "../utils";
|
|
7
|
+
|
|
8
|
+
export type AffixInputVariant = "currency" | "percent" | "email";
|
|
9
|
+
|
|
10
|
+
export interface AffixInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "prefix"> {
|
|
11
|
+
/** Preset: symbol, input type, inputmode and alignment. */
|
|
12
|
+
variant?: AffixInputVariant;
|
|
13
|
+
/** Symbol before the value (overrides the preset). */
|
|
14
|
+
prefix?: ReactNode;
|
|
15
|
+
/** Symbol after the value (overrides the preset). */
|
|
16
|
+
suffix?: ReactNode;
|
|
17
|
+
/** Right-aligned tabular digits. Off by default: the value sits next to the symbol column. */
|
|
18
|
+
alignEnd?: boolean;
|
|
19
|
+
error?: boolean;
|
|
20
|
+
/** Class for the wrapper (`className` goes to the input). */
|
|
21
|
+
groupClassName?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Preset per variant. Email uses type="email" so :user-invalid checks the address.
|
|
25
|
+
const PRESETS: Record<AffixInputVariant, { prefix?: string; suffix?: string; end: boolean; input: InputHTMLAttributes<HTMLInputElement> }> = {
|
|
26
|
+
currency: { prefix: "\u20AC", end: false, input: { type: "text", inputMode: "decimal" } },
|
|
27
|
+
percent: { prefix: "%", end: false, input: { type: "text", inputMode: "decimal" } },
|
|
28
|
+
email: { prefix: "@", end: false, input: { type: "email", autoComplete: "email" } },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** `.oc-input-group.oc-input-group--affix`: symbol (€, %, @) inside the field. */
|
|
32
|
+
export const AffixInput = forwardRef<HTMLInputElement, AffixInputProps>(function AffixInput(
|
|
33
|
+
{ variant, prefix, suffix, alignEnd, error, groupClassName, className, ...rest },
|
|
34
|
+
ref,
|
|
35
|
+
) {
|
|
36
|
+
const preset = variant ? PRESETS[variant] : undefined;
|
|
37
|
+
const before = prefix ?? preset?.prefix;
|
|
38
|
+
const after = suffix ?? preset?.suffix;
|
|
39
|
+
const end = alignEnd ?? preset?.end ?? false;
|
|
40
|
+
return (
|
|
41
|
+
<div className={cx("oc-input-group", "oc-input-group--affix", end && "oc-input-group--end", error && "oc-input-group--error", groupClassName)}>
|
|
42
|
+
{before != null && <span className="oc-input-addon" aria-hidden="true">{before}</span>}
|
|
43
|
+
<input ref={ref} className={cx("oc-input", className)} aria-invalid={error ? true : undefined} {...preset?.input} {...rest} />
|
|
44
|
+
{after != null && <span className="oc-input-addon" aria-hidden="true">{after}</span>}
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
});
|
package/src/components/Alert.tsx
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { forwardRef } from "react";
|
|
2
|
-
import type { HTMLAttributes, ReactNode } from "react";
|
|
3
|
-
import { cx } from "../utils";
|
|
4
|
-
|
|
5
|
-
export type AlertVariant = "info" | "success" | "warning" | "danger";
|
|
6
|
-
|
|
7
|
-
export interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
8
|
-
variant?: AlertVariant;
|
|
9
|
-
/** Heading (`oc-alert-title`). */
|
|
10
|
-
title?: ReactNode;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** `<div class="oc-alert oc-alert--{variant}" role="status">`; children render inside `oc-alert-description`. */
|
|
14
|
-
export const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
|
|
15
|
-
{ variant = "info", title, children, className, role = "status", ...rest },
|
|
16
|
-
ref,
|
|
17
|
-
) {
|
|
18
|
-
return (
|
|
19
|
-
<div ref={ref} role={role} className={cx("oc-alert", `oc-alert--${variant}`, className)} {...rest}>
|
|
20
|
-
{title != null && <h4 className="oc-alert-title">{title}</h4>}
|
|
21
|
-
{children != null && <p className="oc-alert-description">{children}</p>}
|
|
22
|
-
</div>
|
|
23
|
-
);
|
|
24
|
-
});
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
3
|
+
import { cx } from "../utils";
|
|
4
|
+
|
|
5
|
+
export type AlertVariant = "info" | "success" | "warning" | "danger";
|
|
6
|
+
|
|
7
|
+
export interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
8
|
+
variant?: AlertVariant;
|
|
9
|
+
/** Heading (`oc-alert-title`). */
|
|
10
|
+
title?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** `<div class="oc-alert oc-alert--{variant}" role="status">`; children render inside `oc-alert-description`. */
|
|
14
|
+
export const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
|
|
15
|
+
{ variant = "info", title, children, className, role = "status", ...rest },
|
|
16
|
+
ref,
|
|
17
|
+
) {
|
|
18
|
+
return (
|
|
19
|
+
<div ref={ref} role={role} className={cx("oc-alert", `oc-alert--${variant}`, className)} {...rest}>
|
|
20
|
+
{title != null && <h4 className="oc-alert-title">{title}</h4>}
|
|
21
|
+
{children != null && <p className="oc-alert-description">{children}</p>}
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
});
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { forwardRef, useId, useRef } from "react";
|
|
4
|
-
import type { DialogHTMLAttributes, ReactNode } from "react";
|
|
5
|
-
import { attrs, cx } from "../utils";
|
|
6
|
-
import { DialogContext, useDialog, useMergedRef } from "../hooks";
|
|
7
|
-
import type { DialogState } from "../hooks";
|
|
8
|
-
|
|
9
|
-
export type AlertDialogType = "success" | "error" | "warning" | "info" | "question";
|
|
10
|
-
|
|
11
|
-
const GLYPHS: Record<AlertDialogType, string> = {
|
|
12
|
-
success: "✓",
|
|
13
|
-
error: "×",
|
|
14
|
-
warning: "!",
|
|
15
|
-
info: "i",
|
|
16
|
-
question: "?",
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export interface AlertDialogProps extends Omit<DialogHTMLAttributes<HTMLDialogElement>, "title" | "open" | "onClose">, DialogState {
|
|
20
|
-
/** Type modifier on the content box (sets the icon color). Default `warning`. */
|
|
21
|
-
type?: AlertDialogType;
|
|
22
|
-
/** Custom icon glyph or SVG (defaults to a glyph for the type). */
|
|
23
|
-
icon?: ReactNode;
|
|
24
|
-
title: ReactNode;
|
|
25
|
-
/** Message (`oc-modal-body`): pass as `message` or as children. */
|
|
26
|
-
message?: ReactNode;
|
|
27
|
-
/** Secondary + primary buttons (`oc-modal-footer`), e.g. `<ModalClose>`s. */
|
|
28
|
-
footer?: ReactNode;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Confirmation `<dialog class="oc-modal">` with a status icon. */
|
|
32
|
-
export const AlertDialog = forwardRef<HTMLDialogElement, AlertDialogProps>(function AlertDialog(
|
|
33
|
-
{ open, defaultOpen, onOpenChange, type = "warning", icon, title, message, footer, className, children, ...rest },
|
|
34
|
-
forwardedRef,
|
|
35
|
-
) {
|
|
36
|
-
const inner = useRef<HTMLDialogElement>(null);
|
|
37
|
-
const ref = useMergedRef(inner, forwardedRef);
|
|
38
|
-
const id = useId();
|
|
39
|
-
const dlg = useDialog(inner, { open, defaultOpen, onOpenChange });
|
|
40
|
-
return (
|
|
41
|
-
<dialog
|
|
42
|
-
ref={ref}
|
|
43
|
-
className={cx("oc-modal", className)}
|
|
44
|
-
aria-labelledby={`${id}-title`}
|
|
45
|
-
aria-describedby={`${id}-text`}
|
|
46
|
-
{...attrs({ closedby: "closerequest" })}
|
|
47
|
-
{...rest}
|
|
48
|
-
onClose={dlg.onClose}
|
|
49
|
-
>
|
|
50
|
-
<DialogContext.Provider value={{ close: dlg.close }}>
|
|
51
|
-
<div className={`oc-modal-content oc-modal-content--sm oc-alert-dialog oc-alert-dialog--${type}`}>
|
|
52
|
-
<span className="oc-alert-dialog-icon" aria-hidden="true">{icon ?? GLYPHS[type]}</span>
|
|
53
|
-
<h2 className="oc-modal-title" id={`${id}-title`}>{title}</h2>
|
|
54
|
-
<div className="oc-modal-body" id={`${id}-text`}>{message ?? children}</div>
|
|
55
|
-
{footer != null && <div className="oc-modal-footer">{footer}</div>}
|
|
56
|
-
</div>
|
|
57
|
-
</DialogContext.Provider>
|
|
58
|
-
</dialog>
|
|
59
|
-
);
|
|
60
|
-
});
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { forwardRef, useId, useRef } from "react";
|
|
4
|
+
import type { DialogHTMLAttributes, ReactNode } from "react";
|
|
5
|
+
import { attrs, cx } from "../utils";
|
|
6
|
+
import { DialogContext, useDialog, useMergedRef } from "../hooks";
|
|
7
|
+
import type { DialogState } from "../hooks";
|
|
8
|
+
|
|
9
|
+
export type AlertDialogType = "success" | "error" | "warning" | "info" | "question";
|
|
10
|
+
|
|
11
|
+
const GLYPHS: Record<AlertDialogType, string> = {
|
|
12
|
+
success: "✓",
|
|
13
|
+
error: "×",
|
|
14
|
+
warning: "!",
|
|
15
|
+
info: "i",
|
|
16
|
+
question: "?",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export interface AlertDialogProps extends Omit<DialogHTMLAttributes<HTMLDialogElement>, "title" | "open" | "onClose">, DialogState {
|
|
20
|
+
/** Type modifier on the content box (sets the icon color). Default `warning`. */
|
|
21
|
+
type?: AlertDialogType;
|
|
22
|
+
/** Custom icon glyph or SVG (defaults to a glyph for the type). */
|
|
23
|
+
icon?: ReactNode;
|
|
24
|
+
title: ReactNode;
|
|
25
|
+
/** Message (`oc-modal-body`): pass as `message` or as children. */
|
|
26
|
+
message?: ReactNode;
|
|
27
|
+
/** Secondary + primary buttons (`oc-modal-footer`), e.g. `<ModalClose>`s. */
|
|
28
|
+
footer?: ReactNode;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Confirmation `<dialog class="oc-modal">` with a status icon. */
|
|
32
|
+
export const AlertDialog = forwardRef<HTMLDialogElement, AlertDialogProps>(function AlertDialog(
|
|
33
|
+
{ open, defaultOpen, onOpenChange, type = "warning", icon, title, message, footer, className, children, ...rest },
|
|
34
|
+
forwardedRef,
|
|
35
|
+
) {
|
|
36
|
+
const inner = useRef<HTMLDialogElement>(null);
|
|
37
|
+
const ref = useMergedRef(inner, forwardedRef);
|
|
38
|
+
const id = useId();
|
|
39
|
+
const dlg = useDialog(inner, { open, defaultOpen, onOpenChange });
|
|
40
|
+
return (
|
|
41
|
+
<dialog
|
|
42
|
+
ref={ref}
|
|
43
|
+
className={cx("oc-modal", className)}
|
|
44
|
+
aria-labelledby={`${id}-title`}
|
|
45
|
+
aria-describedby={`${id}-text`}
|
|
46
|
+
{...attrs({ closedby: "closerequest" })}
|
|
47
|
+
{...rest}
|
|
48
|
+
onClose={dlg.onClose}
|
|
49
|
+
>
|
|
50
|
+
<DialogContext.Provider value={{ close: dlg.close }}>
|
|
51
|
+
<div className={`oc-modal-content oc-modal-content--sm oc-alert-dialog oc-alert-dialog--${type}`}>
|
|
52
|
+
<span className="oc-alert-dialog-icon" aria-hidden="true">{icon ?? GLYPHS[type]}</span>
|
|
53
|
+
<h2 className="oc-modal-title" id={`${id}-title`}>{title}</h2>
|
|
54
|
+
<div className="oc-modal-body" id={`${id}-text`}>{message ?? children}</div>
|
|
55
|
+
{footer != null && <div className="oc-modal-footer">{footer}</div>}
|
|
56
|
+
</div>
|
|
57
|
+
</DialogContext.Provider>
|
|
58
|
+
</dialog>
|
|
59
|
+
);
|
|
60
|
+
});
|