@umituz/web-design-system 5.3.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/__tests__/cn.test.ts +0 -3
- package/src/__tests__/component-slots.test.tsx +0 -4
- package/src/__tests__/components.smoke.test.tsx +3 -11
- package/src/__tests__/money.test.tsx +0 -3
- package/src/__tests__/productivity-components.test.tsx +135 -0
- package/src/__tests__/styles.contract.test.ts +0 -13
- package/src/domain/tokens/index.ts +0 -7
- package/src/domain/tokens/responsive-map.tokens.ts +18 -107
- package/src/domain/types/breakpoint.types.ts +9 -13
- package/src/domain/types/component.types.ts +0 -4
- package/src/domain/types/index.ts +0 -5
- package/src/global.d.ts +0 -4
- package/src/index.ts +82 -29
- package/src/infrastructure/constants/breakpoint.constants.ts +0 -31
- package/src/infrastructure/constants/component.constants.ts +0 -4
- package/src/infrastructure/constants/index.ts +0 -5
- package/src/infrastructure/constants/size-variant.constants.ts +0 -33
- package/src/infrastructure/constants/spacing.constants.ts +34 -63
- package/src/infrastructure/constants/timing.constants.ts +0 -18
- package/src/infrastructure/constants/typography.constants.ts +22 -54
- package/src/infrastructure/utils/calculation.utils.ts +0 -9
- package/src/infrastructure/utils/cn.util.ts +0 -18
- package/src/infrastructure/utils/hash.util.ts +0 -5
- package/src/infrastructure/utils/index.ts +0 -5
- package/src/infrastructure/utils/money.utils.ts +0 -7
- package/src/infrastructure/utils/responsive.utils.ts +0 -66
- package/src/presentation/atoms/AspectRatio.tsx +0 -10
- package/src/presentation/atoms/Badge.tsx +0 -5
- package/src/presentation/atoms/BatteryPill.tsx +0 -5
- package/src/presentation/atoms/Button.tsx +0 -2
- package/src/presentation/atoms/Checkbox.tsx +0 -4
- package/src/presentation/atoms/CopyButton.tsx +93 -0
- package/src/presentation/atoms/Divider.tsx +0 -4
- package/src/presentation/atoms/HeatmapStrip.tsx +0 -10
- package/src/presentation/atoms/Hide.tsx +0 -24
- package/src/presentation/atoms/Icon.tsx +0 -4
- package/src/presentation/atoms/IconButton.tsx +0 -4
- package/src/presentation/atoms/Input.tsx +0 -4
- package/src/presentation/atoms/Kbd.tsx +0 -4
- package/src/presentation/atoms/Label.tsx +0 -4
- package/src/presentation/atoms/Link.tsx +0 -4
- package/src/presentation/atoms/Money.tsx +0 -6
- package/src/presentation/atoms/Progress.tsx +0 -6
- package/src/presentation/atoms/Radio.tsx +0 -4
- package/src/presentation/atoms/Separator.tsx +0 -4
- package/src/presentation/atoms/Show.tsx +0 -24
- package/src/presentation/atoms/Skeleton.tsx +0 -4
- package/src/presentation/atoms/Slider.tsx +0 -4
- package/src/presentation/atoms/Sparkline.tsx +0 -4
- package/src/presentation/atoms/Spinner.tsx +0 -4
- package/src/presentation/atoms/StatusDot.tsx +79 -0
- package/src/presentation/atoms/Switch.tsx +0 -4
- package/src/presentation/atoms/Text.tsx +0 -4
- package/src/presentation/atoms/Textarea.tsx +0 -4
- package/src/presentation/atoms/Toggle.tsx +0 -4
- package/src/presentation/atoms/Tooltip.tsx +0 -4
- package/src/presentation/atoms/index.ts +9 -9
- package/src/presentation/hooks/index.ts +0 -5
- package/src/presentation/hooks/useClickOutside.ts +0 -6
- package/src/presentation/hooks/useClipboard.ts +0 -4
- package/src/presentation/hooks/useDebounce.ts +0 -8
- package/src/presentation/hooks/useKeyboard.ts +0 -6
- package/src/presentation/hooks/useLocalStorage.ts +0 -4
- package/src/presentation/hooks/useMediaQuery.ts +0 -37
- package/src/presentation/hooks/useScrollLock.ts +0 -5
- package/src/presentation/hooks/useTheme.ts +9 -13
- package/src/presentation/hooks/useToggle.ts +0 -4
- package/src/presentation/molecules/ActiveFilterTags.tsx +0 -4
- package/src/presentation/molecules/Avatar.tsx +0 -5
- package/src/presentation/molecules/BookmarkButton.tsx +0 -15
- package/src/presentation/molecules/CheckboxGroup.tsx +0 -4
- package/src/presentation/molecules/Chip.tsx +0 -5
- package/src/presentation/molecules/CodeBlock.tsx +0 -5
- package/src/presentation/molecules/ConfirmButton.tsx +125 -0
- package/src/presentation/molecules/Field.tsx +0 -7
- package/src/presentation/molecules/FormField.tsx +1 -5
- package/src/presentation/molecules/InputGroup.tsx +0 -4
- package/src/presentation/molecules/ListItem.tsx +0 -4
- package/src/presentation/molecules/OptionTile.tsx +0 -10
- package/src/presentation/molecules/PasswordInput.tsx +43 -0
- package/src/presentation/molecules/RadioGroup.tsx +0 -4
- package/src/presentation/molecules/RatingStars.tsx +0 -8
- package/src/presentation/molecules/ScrollArea.tsx +0 -4
- package/src/presentation/molecules/SearchBox.tsx +0 -4
- package/src/presentation/molecules/SearchInput.tsx +110 -0
- package/src/presentation/molecules/Select.tsx +0 -4
- package/src/presentation/molecules/StatPill.tsx +0 -5
- package/src/presentation/molecules/TagInput.tsx +0 -7
- package/src/presentation/molecules/Textarea.tsx +0 -4
- package/src/presentation/molecules/Toggle.tsx +0 -4
- package/src/presentation/molecules/Toolbar.tsx +36 -0
- package/src/presentation/molecules/index.ts +25 -8
- package/src/presentation/organisms/Accordion.tsx +0 -4
- package/src/presentation/organisms/Alert.tsx +0 -4
- package/src/presentation/organisms/AvatarStack.tsx +0 -5
- package/src/presentation/organisms/Breadcrumb.tsx +0 -7
- package/src/presentation/organisms/Card.tsx +0 -6
- package/src/presentation/organisms/Checklist.tsx +0 -4
- package/src/presentation/organisms/Collapsible.tsx +0 -4
- package/src/presentation/organisms/ConfirmDialog.tsx +0 -8
- package/src/presentation/organisms/CoverArt.tsx +0 -9
- package/src/presentation/organisms/DataTable.tsx +0 -8
- package/src/presentation/organisms/DateRangePicker.tsx +0 -9
- package/src/presentation/organisms/DescriptionList.tsx +113 -0
- package/src/presentation/organisms/Dialog.tsx +0 -4
- package/src/presentation/organisms/Drawer.tsx +0 -6
- package/src/presentation/organisms/DropdownMenu.tsx +0 -4
- package/src/presentation/organisms/ErrorBoundary.tsx +0 -12
- package/src/presentation/organisms/FileUploader.tsx +0 -9
- package/src/presentation/organisms/FilterPopover.tsx +0 -8
- package/src/presentation/organisms/FormModal.tsx +0 -5
- package/src/presentation/organisms/HoverCard.tsx +0 -4
- package/src/presentation/organisms/KeyValueList.tsx +0 -4
- package/src/presentation/organisms/KpiCard.tsx +0 -6
- package/src/presentation/organisms/Lightbox.tsx +0 -7
- package/src/presentation/organisms/LoadingState.tsx +0 -4
- package/src/presentation/organisms/MapPreview.tsx +0 -9
- package/src/presentation/organisms/MetricCard.tsx +1 -5
- package/src/presentation/organisms/Modal.tsx +0 -4
- package/src/presentation/organisms/PhotoGallery.tsx +0 -9
- package/src/presentation/organisms/PlanPicker.tsx +0 -5
- package/src/presentation/organisms/Popover.tsx +0 -4
- package/src/presentation/organisms/Sheet.tsx +0 -4
- package/src/presentation/organisms/StatCard.tsx +0 -5
- package/src/presentation/organisms/StatsGrid.tsx +110 -0
- package/src/presentation/organisms/StatusBadge.tsx +0 -12
- package/src/presentation/organisms/Stepper.tsx +0 -5
- package/src/presentation/organisms/Table.tsx +0 -4
- package/src/presentation/organisms/Tabs.tsx +0 -7
- package/src/presentation/organisms/Timeline.tsx +0 -8
- package/src/presentation/organisms/Toaster.tsx +0 -9
- package/src/presentation/organisms/ToggleGroup.tsx +0 -4
- package/src/presentation/organisms/index.ts +10 -11
- package/src/presentation/templates/CrudPageTemplate.tsx +69 -0
- package/src/presentation/templates/Form.tsx +0 -4
- package/src/presentation/templates/List.tsx +0 -4
- package/src/presentation/templates/PageHeader.tsx +0 -4
- package/src/presentation/templates/PageLayout.tsx +0 -4
- package/src/presentation/templates/ProjectSkeleton.tsx +0 -4
- package/src/presentation/templates/ResponsiveContainer.tsx +0 -43
- package/src/presentation/templates/Section.tsx +0 -4
- package/src/presentation/templates/SettingsLayout.tsx +102 -0
- package/src/presentation/templates/index.ts +7 -6
- package/src/styles/app.css +0 -18
- package/src/styles/bridge.css +0 -34
- package/src/styles/compile.css +0 -13
- package/src/styles/source.css +0 -12
- package/src/styles/theme.css +0 -27
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TagInput Component (Molecule)
|
|
3
|
-
* @description Controlled free-form tag entry rendered as removable chips.
|
|
4
|
-
* Type and press Enter (or comma) to commit, Backspace on an empty draft removes the last tag,
|
|
5
|
-
* duplicates are ignored, and blur commits by default.
|
|
6
|
-
*/
|
|
7
1
|
|
|
8
2
|
import { forwardRef, useState } from 'react';
|
|
9
3
|
import type { InputHTMLAttributes } from 'react';
|
|
@@ -16,7 +10,6 @@ export interface TagInputProps
|
|
|
16
10
|
BaseProps {
|
|
17
11
|
tags: string[];
|
|
18
12
|
onTagsChange: (tags: string[]) => void;
|
|
19
|
-
/** Commit the draft on blur (default true). */
|
|
20
13
|
commitOnBlur?: boolean;
|
|
21
14
|
}
|
|
22
15
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { cn } from '../../infrastructure/utils';
|
|
3
|
+
|
|
4
|
+
export interface ToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
search?: React.ReactNode;
|
|
6
|
+
filters?: React.ReactNode;
|
|
7
|
+
actions?: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Toolbar = React.forwardRef<HTMLDivElement, ToolbarProps>(
|
|
11
|
+
({ search, filters, actions, children, className, ...props }, ref) => {
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
ref={ref}
|
|
15
|
+
className={cn(
|
|
16
|
+
'flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-3 w-full',
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
<div className="flex flex-1 flex-wrap items-center gap-2">
|
|
22
|
+
{search && <div className="w-full sm:w-72 max-w-full">{search}</div>}
|
|
23
|
+
{filters && <div className="flex flex-wrap items-center gap-2">{filters}</div>}
|
|
24
|
+
{children}
|
|
25
|
+
</div>
|
|
26
|
+
{actions && (
|
|
27
|
+
<div className="flex items-center gap-2 shrink-0 justify-end">
|
|
28
|
+
{actions}
|
|
29
|
+
</div>
|
|
30
|
+
)}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
Toolbar.displayName = 'Toolbar';
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Molecules Export
|
|
3
|
-
* @description Molecule components - combinations of atoms
|
|
4
|
-
* Subpath: @umituz/web-design-system/molecules
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
1
|
export { FormField } from './FormField';
|
|
8
2
|
export type { FormFieldProps } from './FormField';
|
|
9
3
|
|
|
@@ -22,7 +16,18 @@ export type { ChipProps } from './Chip';
|
|
|
22
16
|
export { Toggle } from './Toggle';
|
|
23
17
|
export type { ToggleProps } from './Toggle';
|
|
24
18
|
|
|
25
|
-
export {
|
|
19
|
+
export {
|
|
20
|
+
Select,
|
|
21
|
+
SelectGroup,
|
|
22
|
+
SelectValue,
|
|
23
|
+
SelectTrigger,
|
|
24
|
+
SelectContent,
|
|
25
|
+
SelectLabel,
|
|
26
|
+
SelectItem,
|
|
27
|
+
SelectSeparator,
|
|
28
|
+
SelectScrollUpButton,
|
|
29
|
+
SelectScrollDownButton,
|
|
30
|
+
} from './Select';
|
|
26
31
|
|
|
27
32
|
export { Textarea } from './Textarea';
|
|
28
33
|
export type { TextareaProps } from './Textarea';
|
|
@@ -41,7 +46,6 @@ export { ScrollArea, ScrollBar } from './ScrollArea';
|
|
|
41
46
|
export { ListItem } from './ListItem';
|
|
42
47
|
export type { ListItemProps } from './ListItem';
|
|
43
48
|
|
|
44
|
-
// NEW: Filter & Navigation Components
|
|
45
49
|
export { ActiveFilterTags } from './ActiveFilterTags';
|
|
46
50
|
export type { ActiveFilterTagsProps } from './ActiveFilterTags';
|
|
47
51
|
|
|
@@ -70,3 +74,16 @@ export type { TagInputProps } from './TagInput';
|
|
|
70
74
|
|
|
71
75
|
export { MarkdownText } from './MarkdownText';
|
|
72
76
|
export type { MarkdownTextProps } from './MarkdownText';
|
|
77
|
+
|
|
78
|
+
// New Productivity Molecules
|
|
79
|
+
export { PasswordInput } from './PasswordInput';
|
|
80
|
+
export type { PasswordInputProps } from './PasswordInput';
|
|
81
|
+
|
|
82
|
+
export { SearchInput } from './SearchInput';
|
|
83
|
+
export type { SearchInputProps } from './SearchInput';
|
|
84
|
+
|
|
85
|
+
export { ConfirmButton } from './ConfirmButton';
|
|
86
|
+
export type { ConfirmButtonProps } from './ConfirmButton';
|
|
87
|
+
|
|
88
|
+
export { Toolbar } from './Toolbar';
|
|
89
|
+
export type { ToolbarProps } from './Toolbar';
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AvatarStack Component (Organism)
|
|
3
|
-
* @description Overlapping avatar row built on the Avatar molecule with an overflow count badge
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { forwardRef } from 'react';
|
|
7
3
|
import { cn } from '../../infrastructure/utils';
|
|
@@ -16,7 +12,6 @@ export interface AvatarPerson {
|
|
|
16
12
|
|
|
17
13
|
export interface AvatarStackProps extends BaseProps {
|
|
18
14
|
people: AvatarPerson[];
|
|
19
|
-
/** Avatars shown before the +N badge (default 4). */
|
|
20
15
|
max?: number;
|
|
21
16
|
size?: ExtendedSizes;
|
|
22
17
|
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Breadcrumb Component (Organism)
|
|
3
|
-
* @description Navigation breadcrumb trail
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';
|
|
7
3
|
import { cn } from '../../infrastructure/utils';
|
|
@@ -13,9 +9,6 @@ export interface BreadcrumbItem {
|
|
|
13
9
|
label: string;
|
|
14
10
|
href?: string;
|
|
15
11
|
icon?: ReactNode;
|
|
16
|
-
/** Renders this node instead of the default anchor — for hosts that need
|
|
17
|
-
* SPA router links (react-router `Link`, Next `Link`, …). When set, label
|
|
18
|
-
* and href are ignored for rendering. */
|
|
19
12
|
content?: ReactNode;
|
|
20
13
|
}
|
|
21
14
|
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Card Component (Organism)
|
|
3
|
-
* @description Content container with header, body, footer
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { forwardRef, type HTMLAttributes } from 'react';
|
|
7
3
|
import React from 'react';
|
|
@@ -11,9 +7,7 @@ import type { BaseProps } from '../../domain/types';
|
|
|
11
7
|
|
|
12
8
|
export interface CardProps extends HTMLAttributes<HTMLDivElement>, BaseProps {
|
|
13
9
|
variant?: 'default' | 'outlined' | 'elevated';
|
|
14
|
-
/** Render as the single child element (e.g. a router Link) instead of a div. */
|
|
15
10
|
asChild?: boolean;
|
|
16
|
-
/** Interactive card: pointer cursor, hover border and a visible focus ring. */
|
|
17
11
|
interactive?: boolean;
|
|
18
12
|
}
|
|
19
13
|
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ConfirmDialog Component (Organism)
|
|
3
|
-
* @description Destructive/primary confirmation built on Modal: title, body and
|
|
4
|
-
* Cancel + confirm actions; unmounts itself after confirming
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
2
|
import type { ReactNode } from 'react';
|
|
8
3
|
import { Button } from '../atoms/Button';
|
|
@@ -13,11 +8,8 @@ export interface ConfirmDialogProps {
|
|
|
13
8
|
body?: ReactNode;
|
|
14
9
|
confirmLabel?: string;
|
|
15
10
|
cancelLabel?: string;
|
|
16
|
-
/** Visual weight of the confirm action; defaults to danger. */
|
|
17
11
|
tone?: 'danger' | 'primary' | 'success';
|
|
18
|
-
/** Renders nothing when false; defaults to true (conditional mounting also works). */
|
|
19
12
|
open?: boolean;
|
|
20
|
-
/** While true the confirm action is locked and shows its loading state; the caller owns dismissal. */
|
|
21
13
|
busy?: boolean;
|
|
22
14
|
onConfirm: () => void;
|
|
23
15
|
onClose: () => void;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CoverArt Component (Organism)
|
|
3
|
-
* @description Deterministic generated artwork for entities without
|
|
4
|
-
* photography — the seed always resolves to the same gradient, replaced by
|
|
5
|
-
* real imagery once a media service exists
|
|
6
|
-
*/
|
|
7
1
|
|
|
8
2
|
import type { ReactNode } from 'react';
|
|
9
3
|
import { cn } from '../../infrastructure/utils';
|
|
@@ -11,13 +5,10 @@ import { hashSeed } from '../../infrastructure/utils/hash.util';
|
|
|
11
5
|
import type { BaseProps } from '../../domain/types';
|
|
12
6
|
|
|
13
7
|
export interface CoverArtProps extends BaseProps {
|
|
14
|
-
/** Any stable entity id/name — the same seed always renders the same gradient. */
|
|
15
8
|
seed: string;
|
|
16
9
|
ratio?: '16/9' | '3/2' | '4/3' | '1/1' | '21/9';
|
|
17
10
|
rounded?: 'none' | 'md' | 'lg' | 'top';
|
|
18
|
-
/** Brand palette override ([from, to]); wins over the seeded gradient. */
|
|
19
11
|
colors?: readonly [string, string];
|
|
20
|
-
/** Accessible description; omit for purely decorative usage. */
|
|
21
12
|
alt?: string;
|
|
22
13
|
children?: ReactNode;
|
|
23
14
|
}
|
|
@@ -51,17 +51,14 @@ export interface SortState {
|
|
|
51
51
|
export interface Column<T> {
|
|
52
52
|
id: string;
|
|
53
53
|
header: ReactNode;
|
|
54
|
-
/** Data source; optional when the column renders through `cell` only. */
|
|
55
54
|
accessor?: keyof T | ((row: T) => ReactNode);
|
|
56
55
|
cell?: (row: T) => ReactNode;
|
|
57
56
|
sortValue?: (row: T) => string | number;
|
|
58
57
|
sortable?: boolean;
|
|
59
58
|
align?: ColumnAlign;
|
|
60
59
|
hideBelow?: ColumnHideBelow;
|
|
61
|
-
/** Pixels when numeric. */
|
|
62
60
|
width?: string | number;
|
|
63
61
|
className?: string;
|
|
64
|
-
/** Hidden until the reader enables it from the built-in columns menu. */
|
|
65
62
|
optional?: boolean;
|
|
66
63
|
}
|
|
67
64
|
|
|
@@ -71,12 +68,9 @@ export interface TablePaginationProps extends BaseProps {
|
|
|
71
68
|
onPage: (page: number) => void;
|
|
72
69
|
label?: ReactNode;
|
|
73
70
|
hideWhenSingle?: boolean;
|
|
74
|
-
/** Page-size choices offered in the rows-per-page select. */
|
|
75
71
|
pageSizeOptions?: readonly number[];
|
|
76
|
-
/** Currently selected page size; required with `pageSizeOptions`. */
|
|
77
72
|
pageSize?: number;
|
|
78
73
|
onPageSize?: (size: number) => void;
|
|
79
|
-
/** Accessible label for the rows-per-page select (default "Rows per page"). */
|
|
80
74
|
pageSizeLabel?: ReactNode;
|
|
81
75
|
}
|
|
82
76
|
|
|
@@ -103,7 +97,6 @@ export interface DataTableProps<T> extends BaseProps {
|
|
|
103
97
|
stackable?: boolean;
|
|
104
98
|
paginated?: boolean;
|
|
105
99
|
pageSize?: number;
|
|
106
|
-
/** Page-size choices offered in the footer rows-per-page select. */
|
|
107
100
|
pageSizeOptions?: readonly number[];
|
|
108
101
|
onPageSizeChange?: (size: number) => void;
|
|
109
102
|
emptyState?: {
|
|
@@ -112,7 +105,6 @@ export interface DataTableProps<T> extends BaseProps {
|
|
|
112
105
|
};
|
|
113
106
|
onRowClick?: (row: T) => void;
|
|
114
107
|
rowLabel?: (row: T) => string;
|
|
115
|
-
/** Row-level class, e.g. a tone accent for overdue rows. */
|
|
116
108
|
rowClass?: (row: T) => string | undefined;
|
|
117
109
|
}
|
|
118
110
|
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DateRangePicker Component (Organism)
|
|
3
|
-
* @description Compact preset-driven date range picker with custom date
|
|
4
|
-
* inputs, built on the DropdownMenu + Input atoms.
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
2
|
import { useState } from 'react';
|
|
8
3
|
import { CalendarRange } from 'lucide-react';
|
|
@@ -20,7 +15,6 @@ import {
|
|
|
20
15
|
export interface DateRange {
|
|
21
16
|
from: string;
|
|
22
17
|
to: string;
|
|
23
|
-
/** preset id when the range was chosen from the preset list */
|
|
24
18
|
preset?: RangePreset;
|
|
25
19
|
}
|
|
26
20
|
|
|
@@ -56,7 +50,6 @@ function shiftMonths(iso: string, months: number): string {
|
|
|
56
50
|
return d.toISOString().slice(0, 10);
|
|
57
51
|
}
|
|
58
52
|
|
|
59
|
-
/** Resolve a preset id into a concrete range around `today` (ISO date). */
|
|
60
53
|
export function presetRange(preset: RangePreset, today: string = isoToday()): DateRange {
|
|
61
54
|
switch (preset) {
|
|
62
55
|
case '7d':
|
|
@@ -79,9 +72,7 @@ export function presetRange(preset: RangePreset, today: string = isoToday()): Da
|
|
|
79
72
|
export interface DateRangePickerProps {
|
|
80
73
|
value: DateRange;
|
|
81
74
|
onChange: (range: DateRange) => void;
|
|
82
|
-
/** Preset list shown above the custom inputs; defaults to DEFAULT_RANGE_PRESETS. */
|
|
83
75
|
presets?: readonly RangePresetOption[];
|
|
84
|
-
/** Day formatter for the trigger label; defaults to the raw ISO date. */
|
|
85
76
|
formatDay?: (iso: string) => string;
|
|
86
77
|
'aria-label'?: string;
|
|
87
78
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Card } from './Card';
|
|
3
|
+
import { Badge, type BadgeVariant } from '../atoms/Badge';
|
|
4
|
+
import { CopyButton } from '../atoms/CopyButton';
|
|
5
|
+
import { cn } from '../../infrastructure/utils';
|
|
6
|
+
|
|
7
|
+
export interface DescriptionItem {
|
|
8
|
+
key?: string;
|
|
9
|
+
label: string;
|
|
10
|
+
value: React.ReactNode;
|
|
11
|
+
copyable?: boolean;
|
|
12
|
+
copyText?: string;
|
|
13
|
+
badge?: string;
|
|
14
|
+
badgeVariant?: BadgeVariant;
|
|
15
|
+
hint?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DescriptionListProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
19
|
+
title?: React.ReactNode;
|
|
20
|
+
description?: React.ReactNode;
|
|
21
|
+
action?: React.ReactNode;
|
|
22
|
+
items: DescriptionItem[];
|
|
23
|
+
columns?: 1 | 2;
|
|
24
|
+
bordered?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const DescriptionList = React.forwardRef<HTMLDivElement, DescriptionListProps>(
|
|
28
|
+
(
|
|
29
|
+
{
|
|
30
|
+
title,
|
|
31
|
+
description,
|
|
32
|
+
action,
|
|
33
|
+
items,
|
|
34
|
+
columns = 1,
|
|
35
|
+
bordered = true,
|
|
36
|
+
className,
|
|
37
|
+
...props
|
|
38
|
+
},
|
|
39
|
+
ref
|
|
40
|
+
) => {
|
|
41
|
+
const content = (
|
|
42
|
+
<div
|
|
43
|
+
ref={ref}
|
|
44
|
+
className={cn('w-full flex flex-col gap-4', className)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
{(title || action || description) && (
|
|
48
|
+
<div className="flex items-start justify-between gap-4 pb-3 border-b border-border">
|
|
49
|
+
<div>
|
|
50
|
+
{title && (
|
|
51
|
+
<h3 className="text-base font-semibold text-foreground tracking-tight">
|
|
52
|
+
{title}
|
|
53
|
+
</h3>
|
|
54
|
+
)}
|
|
55
|
+
{description && (
|
|
56
|
+
<p className="text-sm text-muted-foreground mt-0.5">
|
|
57
|
+
{description}
|
|
58
|
+
</p>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
{action && <div className="shrink-0">{action}</div>}
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
|
|
65
|
+
<dl
|
|
66
|
+
className={cn(
|
|
67
|
+
'grid gap-x-6 gap-y-3',
|
|
68
|
+
columns === 2 ? 'grid-cols-1 sm:grid-cols-2' : 'grid-cols-1'
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
{items.map((item, idx) => {
|
|
72
|
+
const rawCopyText = item.copyText ?? (typeof item.value === 'string' ? item.value : '');
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div
|
|
76
|
+
key={item.key ?? idx}
|
|
77
|
+
className="flex items-center justify-between py-2 border-b border-border/60 last:border-0 gap-3"
|
|
78
|
+
>
|
|
79
|
+
<dt className="text-sm font-medium text-muted-foreground shrink-0">
|
|
80
|
+
{item.label}
|
|
81
|
+
{item.hint && (
|
|
82
|
+
<span className="block text-xs font-normal text-muted-foreground/75">
|
|
83
|
+
{item.hint}
|
|
84
|
+
</span>
|
|
85
|
+
)}
|
|
86
|
+
</dt>
|
|
87
|
+
<dd className="text-sm font-medium text-foreground flex items-center gap-2 text-right break-all">
|
|
88
|
+
{item.value}
|
|
89
|
+
{item.badge && (
|
|
90
|
+
<Badge variant={item.badgeVariant ?? 'secondary'} size="sm">
|
|
91
|
+
{item.badge}
|
|
92
|
+
</Badge>
|
|
93
|
+
)}
|
|
94
|
+
{item.copyable && rawCopyText && (
|
|
95
|
+
<CopyButton text={rawCopyText} size="xs" />
|
|
96
|
+
)}
|
|
97
|
+
</dd>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
})}
|
|
101
|
+
</dl>
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
if (bordered) {
|
|
106
|
+
return <Card className="p-5">{content}</Card>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return content;
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
DescriptionList.displayName = 'DescriptionList';
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drawer Component (Organism)
|
|
3
|
-
* @description Slide-over panel built on the Sheet compound with a
|
|
4
|
-
* Modal-like prop surface (title, footer, side presets, wide variant)
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
2
|
import type { ReactNode } from 'react';
|
|
8
3
|
import { cn } from '../../infrastructure/utils';
|
|
@@ -21,7 +16,6 @@ export interface DrawerProps {
|
|
|
21
16
|
open: boolean;
|
|
22
17
|
onClose: () => void;
|
|
23
18
|
title?: ReactNode;
|
|
24
|
-
/** Small overline above the title, e.g. an entity code. */
|
|
25
19
|
eyebrow?: ReactNode;
|
|
26
20
|
subtitle?: ReactNode;
|
|
27
21
|
description?: ReactNode;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ErrorBoundary Component (Organism)
|
|
3
|
-
* @description Route/tree-level crash catcher — catches render errors and
|
|
4
|
-
* shows an honest recovery screen instead of a blank page.
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
2
|
import { Component, type ErrorInfo, type ReactNode } from 'react';
|
|
8
3
|
import { AlertTriangle } from 'lucide-react';
|
|
@@ -10,19 +5,12 @@ import { Button } from '../atoms/Button';
|
|
|
10
5
|
|
|
11
6
|
export interface ErrorBoundaryProps {
|
|
12
7
|
children: ReactNode;
|
|
13
|
-
/** Small kicker line above the title (e.g. "Something broke"). */
|
|
14
8
|
kicker?: string;
|
|
15
|
-
/** Heading on the crash screen. */
|
|
16
9
|
title?: string;
|
|
17
|
-
/** Secondary line — defaults to the caught error's message. */
|
|
18
10
|
description?: ReactNode;
|
|
19
|
-
/** Reset (retry) button label; omit to hide the button. */
|
|
20
11
|
resetLabel?: string;
|
|
21
|
-
/** Extra action rendered next to reset (e.g. a link back home). */
|
|
22
12
|
action?: ReactNode;
|
|
23
|
-
/** Fully custom crash screen — receives the error and the reset handler. */
|
|
24
13
|
fallback?: (error: Error, reset: () => void) => ReactNode;
|
|
25
|
-
/** Side-channel for logging services (defaults to console.error). */
|
|
26
14
|
onError?: (error: Error, info: ErrorInfo) => void;
|
|
27
15
|
}
|
|
28
16
|
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FileUploader Component (Organism)
|
|
3
|
-
* @description Keyboard-accessible drag-and-drop file picker with a queued
|
|
4
|
-
* file preview list. Visual only — storage wiring belongs to the host.
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
2
|
import { useRef, useState, type DragEvent } from 'react';
|
|
8
3
|
import { UploadCloud } from 'lucide-react';
|
|
@@ -10,15 +5,11 @@ import { Button } from '../atoms/Button';
|
|
|
10
5
|
import { cn } from '../../infrastructure/utils';
|
|
11
6
|
|
|
12
7
|
export interface FileUploaderProps {
|
|
13
|
-
/** Drop-zone headline. */
|
|
14
8
|
label?: string;
|
|
15
|
-
/** Secondary line under the headline. */
|
|
16
9
|
hint?: string;
|
|
17
10
|
accept?: string;
|
|
18
11
|
multiple?: boolean;
|
|
19
|
-
/** Called with the picked/dropped files. */
|
|
20
12
|
onFiles?: (files: File[]) => void;
|
|
21
|
-
/** How many queued names to list before stopping (visual cap only). */
|
|
22
13
|
maxPreview?: number;
|
|
23
14
|
}
|
|
24
15
|
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FilterPopover Component (Organism)
|
|
3
|
-
* @description List-toolbar filter trigger with an active-filter count badge;
|
|
4
|
-
* hosts arbitrary filter controls inside a Popover
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
2
|
import { useState, type ReactNode } from 'react';
|
|
8
3
|
import { Filter } from 'lucide-react';
|
|
@@ -11,11 +6,8 @@ import { Button } from '../atoms/Button';
|
|
|
11
6
|
import { Popover, PopoverContent, PopoverTrigger } from './Popover';
|
|
12
7
|
|
|
13
8
|
export interface FilterPopoverProps {
|
|
14
|
-
/** Trigger caption; defaults to "Filters". */
|
|
15
9
|
label?: string;
|
|
16
|
-
/** Active filter count; rendered as a badge when greater than zero. */
|
|
17
10
|
count?: number;
|
|
18
|
-
/** Popover alignment relative to the trigger; defaults to "start". */
|
|
19
11
|
align?: 'start' | 'center' | 'end';
|
|
20
12
|
children: ReactNode;
|
|
21
13
|
className?: string;
|
|
@@ -65,8 +65,6 @@ export interface FormModalMessages {
|
|
|
65
65
|
invalidNumber: string;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
/** Resolves a field's options — static arrays pass through, functions
|
|
69
|
-
* receive the current values so a select can depend on earlier fields. */
|
|
70
68
|
function fieldOptions(f: FormModalField, values: FormModalValues): FormModalOption[] {
|
|
71
69
|
return typeof f.options === 'function' ? f.options(values) : (f.options ?? []);
|
|
72
70
|
}
|
|
@@ -118,8 +116,6 @@ export function FormModal(
|
|
|
118
116
|
const set = (name: string, val: string | number | boolean) => {
|
|
119
117
|
const next: FormModalValues = { ...values, [name]: val };
|
|
120
118
|
const cleared = [name];
|
|
121
|
-
// A dependent select must never hold a value its resolved options no
|
|
122
|
-
// longer contain — snap it to the first option (or '') and clear its error.
|
|
123
119
|
for (const s of steps) {
|
|
124
120
|
for (const f of s.fields) {
|
|
125
121
|
if (f.type !== 'select' || typeof f.options !== 'function') continue;
|
|
@@ -215,7 +211,6 @@ export function FormModal(
|
|
|
215
211
|
{f.label}
|
|
216
212
|
{f.required && (
|
|
217
213
|
<span className="ml-0.5 text-destructive" aria-hidden="true">
|
|
218
|
-
*
|
|
219
214
|
</span>
|
|
220
215
|
)}
|
|
221
216
|
</label>
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KpiCard Component (Organism)
|
|
3
|
-
* @description Compact key-metric card: label, value, delta badge, hint and sparkline
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';
|
|
7
3
|
import { TrendingUp, TrendingDown } from 'lucide-react';
|
|
@@ -34,11 +30,9 @@ export interface KpiCardProps extends HTMLAttributes<HTMLDivElement>, BaseProps
|
|
|
34
30
|
hint?: ReactNode;
|
|
35
31
|
icon?: ReactNode;
|
|
36
32
|
iconTone?: KpiIconTone;
|
|
37
|
-
/** Paints the metric value itself, e.g. red for an over-budget figure. */
|
|
38
33
|
valueTone?: KpiIconTone;
|
|
39
34
|
spark?: readonly number[];
|
|
40
35
|
sparkTone?: KpiIconTone;
|
|
41
|
-
/** Danger emphasis for metrics that need attention, e.g. open incidents. */
|
|
42
36
|
alert?: boolean;
|
|
43
37
|
onClick?: () => void;
|
|
44
38
|
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lightbox Component (Organism)
|
|
3
|
-
* @description Media overlay on the Dialog primitives — click to zoom,
|
|
4
|
-
* caption below, close button in the corner.
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
2
|
import { useState } from 'react';
|
|
8
3
|
import type { ReactNode } from 'react';
|
|
@@ -12,10 +7,8 @@ import { Dialog, DialogContent, DialogTitle } from './Dialog';
|
|
|
12
7
|
export interface LightboxProps {
|
|
13
8
|
open: boolean;
|
|
14
9
|
onClose: () => void;
|
|
15
|
-
/** Accessible name for the media being shown. */
|
|
16
10
|
alt: string;
|
|
17
11
|
caption?: ReactNode;
|
|
18
|
-
/** Toggle zoom on media click; default true. */
|
|
19
12
|
zoomable?: boolean;
|
|
20
13
|
children?: ReactNode;
|
|
21
14
|
className?: string;
|