@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,68 +1,45 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spacing Constants
|
|
3
|
-
* @description Tailwind CSS spacing scale values
|
|
4
|
-
* @see https://tailwindcss.com/docs/customizing-spacing
|
|
5
|
-
*
|
|
6
|
-
* All spacing values used in the design system are defined here.
|
|
7
|
-
* This provides a single source of truth for spacing scale.
|
|
8
|
-
*/
|
|
9
1
|
|
|
10
|
-
/**
|
|
11
|
-
* Tailwind CSS spacing scale
|
|
12
|
-
* Each value represents a spacing unit that maps to a specific pixel value
|
|
13
|
-
* Format: Key is the spacing class name, value is the unit number (as string for Tailwind class construction)
|
|
14
|
-
*/
|
|
15
2
|
export const SPACING_SCALE = {
|
|
16
|
-
'0': '0',
|
|
17
|
-
'0.5': '0.5',
|
|
18
|
-
'1': '1',
|
|
19
|
-
'1.5': '1.5',
|
|
20
|
-
'2': '2',
|
|
21
|
-
'2.5': '2.5',
|
|
22
|
-
'3': '3',
|
|
23
|
-
'3.5': '3.5',
|
|
24
|
-
'4': '4',
|
|
25
|
-
'5': '5',
|
|
26
|
-
'6': '6',
|
|
27
|
-
'7': '7',
|
|
28
|
-
'8': '8',
|
|
29
|
-
'9': '9',
|
|
30
|
-
'10': '10',
|
|
31
|
-
'11': '11',
|
|
32
|
-
'12': '12',
|
|
33
|
-
'14': '14',
|
|
34
|
-
'16': '16',
|
|
35
|
-
'20': '20',
|
|
36
|
-
'24': '24',
|
|
37
|
-
'28': '28',
|
|
38
|
-
'32': '32',
|
|
39
|
-
'36': '36',
|
|
40
|
-
'40': '40',
|
|
41
|
-
'44': '44',
|
|
42
|
-
'48': '48',
|
|
43
|
-
'52': '52',
|
|
44
|
-
'56': '56',
|
|
45
|
-
'60': '60',
|
|
46
|
-
'64': '64',
|
|
47
|
-
'72': '72',
|
|
48
|
-
'80': '80',
|
|
49
|
-
'96': '96',
|
|
3
|
+
'0': '0',
|
|
4
|
+
'0.5': '0.5',
|
|
5
|
+
'1': '1',
|
|
6
|
+
'1.5': '1.5',
|
|
7
|
+
'2': '2',
|
|
8
|
+
'2.5': '2.5',
|
|
9
|
+
'3': '3',
|
|
10
|
+
'3.5': '3.5',
|
|
11
|
+
'4': '4',
|
|
12
|
+
'5': '5',
|
|
13
|
+
'6': '6',
|
|
14
|
+
'7': '7',
|
|
15
|
+
'8': '8',
|
|
16
|
+
'9': '9',
|
|
17
|
+
'10': '10',
|
|
18
|
+
'11': '11',
|
|
19
|
+
'12': '12',
|
|
20
|
+
'14': '14',
|
|
21
|
+
'16': '16',
|
|
22
|
+
'20': '20',
|
|
23
|
+
'24': '24',
|
|
24
|
+
'28': '28',
|
|
25
|
+
'32': '32',
|
|
26
|
+
'36': '36',
|
|
27
|
+
'40': '40',
|
|
28
|
+
'44': '44',
|
|
29
|
+
'48': '48',
|
|
30
|
+
'52': '52',
|
|
31
|
+
'56': '56',
|
|
32
|
+
'60': '60',
|
|
33
|
+
'64': '64',
|
|
34
|
+
'72': '72',
|
|
35
|
+
'80': '80',
|
|
36
|
+
'96': '96',
|
|
50
37
|
} as const;
|
|
51
38
|
|
|
52
|
-
/**
|
|
53
|
-
* Type for spacing scale keys
|
|
54
|
-
*/
|
|
55
39
|
export type SpacingScaleKey = keyof typeof SPACING_SCALE;
|
|
56
40
|
|
|
57
|
-
/**
|
|
58
|
-
* Type for spacing scale values (string format for Tailwind classes)
|
|
59
|
-
*/
|
|
60
41
|
export type SpacingScale = `${SpacingScaleKey}`;
|
|
61
42
|
|
|
62
|
-
/**
|
|
63
|
-
* Common spacing values used throughout the design system
|
|
64
|
-
* These are frequently used spacing values
|
|
65
|
-
*/
|
|
66
43
|
export const COMMON_SPACING = {
|
|
67
44
|
xs: SPACING_SCALE['1'],
|
|
68
45
|
sm: SPACING_SCALE['2'],
|
|
@@ -72,9 +49,6 @@ export const COMMON_SPACING = {
|
|
|
72
49
|
'2xl': SPACING_SCALE['12'],
|
|
73
50
|
} as const;
|
|
74
51
|
|
|
75
|
-
/**
|
|
76
|
-
* Spacing scale for component sizing
|
|
77
|
-
*/
|
|
78
52
|
export const SIZE_SPACING = {
|
|
79
53
|
xs: SPACING_SCALE['3'],
|
|
80
54
|
sm: SPACING_SCALE['4'],
|
|
@@ -84,9 +58,6 @@ export const SIZE_SPACING = {
|
|
|
84
58
|
'2xl': SPACING_SCALE['10'],
|
|
85
59
|
} as const;
|
|
86
60
|
|
|
87
|
-
/**
|
|
88
|
-
* Validate if a value is a valid spacing scale
|
|
89
|
-
*/
|
|
90
61
|
export function isValidSpacing(value: string): value is SpacingScale {
|
|
91
62
|
return Object.keys(SPACING_SCALE).includes(value);
|
|
92
63
|
}
|
|
@@ -1,45 +1,27 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Timing Constants
|
|
3
|
-
* @description Centralized timing values for debouncing, throttling, transitions, and async operations
|
|
4
|
-
*
|
|
5
|
-
* All durations are in milliseconds unless suffixed with `_MS` (also ms) or `_SECONDS`.
|
|
6
|
-
*/
|
|
7
1
|
|
|
8
2
|
export const TIMING = {
|
|
9
|
-
/** Default debounce window for value-driven hooks */
|
|
10
3
|
DEBOUNCE_DEFAULT_MS: 500,
|
|
11
4
|
|
|
12
|
-
/** Default throttle window for rate-limited functions */
|
|
13
5
|
THROTTLE_DEFAULT_MS: 300,
|
|
14
6
|
|
|
15
|
-
/** Default throttle for resize-driven breakpoint detection */
|
|
16
7
|
RESIZE_DEBOUNCE_MS: 100,
|
|
17
8
|
|
|
18
|
-
/** Tooltip open/close transition delay */
|
|
19
9
|
TOOLTIP_DEFAULT_DELAY_MS: 200,
|
|
20
10
|
|
|
21
|
-
/** Reset window after clipboard copy success */
|
|
22
11
|
CLIPBOARD_RESET_MS: 2000,
|
|
23
12
|
|
|
24
|
-
/** Modal fade-out duration before unmounting */
|
|
25
13
|
MODAL_FADE_OUT_MS: 200,
|
|
26
14
|
|
|
27
|
-
/** CodeBlock "Copied!" indicator reset window */
|
|
28
15
|
CODE_BLOCK_COPY_RESET_MS: 2000,
|
|
29
16
|
|
|
30
|
-
/** CodeBlock collapse threshold (line count) */
|
|
31
17
|
CODE_BLOCK_COLLAPSE_THRESHOLD: 10,
|
|
32
18
|
|
|
33
|
-
/** Newsletter submission simulated network round-trip */
|
|
34
19
|
NEWSLETTER_SUBMIT_DELAY_MS: 1000,
|
|
35
20
|
|
|
36
|
-
/** Memory-leak detector lifespan polling cadence */
|
|
37
21
|
MEMORY_LEAK_POLL_MS: 1000,
|
|
38
22
|
|
|
39
|
-
/** Max render-time samples kept in monitor ring buffer */
|
|
40
23
|
PERFORMANCE_RENDER_TIME_SAMPLES: 50,
|
|
41
24
|
|
|
42
|
-
/** Slow-render threshold for warnings (ms, 1 frame at 60fps) */
|
|
43
25
|
SLOW_RENDER_THRESHOLD_MS: 16,
|
|
44
26
|
} as const;
|
|
45
27
|
|
|
@@ -1,68 +1,40 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Typography Constants
|
|
3
|
-
* @description Typography scale values for the design system
|
|
4
|
-
*
|
|
5
|
-
* All font size and text utility values are defined here.
|
|
6
|
-
* This provides consistency with Tailwind's default typography scale.
|
|
7
|
-
*/
|
|
8
1
|
|
|
9
|
-
/**
|
|
10
|
-
* Text size utilities (Tailwind text classes)
|
|
11
|
-
*/
|
|
12
2
|
export const TEXT_SIZES = {
|
|
13
|
-
xs: 'text-xs',
|
|
14
|
-
sm: 'text-sm',
|
|
15
|
-
base: 'text-base',
|
|
16
|
-
lg: 'text-lg',
|
|
17
|
-
xl: 'text-xl',
|
|
18
|
-
'2xl': 'text-2xl',
|
|
19
|
-
'3xl': 'text-3xl',
|
|
20
|
-
'4xl': 'text-4xl',
|
|
21
|
-
'5xl': 'text-5xl',
|
|
22
|
-
'6xl': 'text-6xl',
|
|
23
|
-
'7xl': 'text-7xl',
|
|
24
|
-
'8xl': 'text-8xl',
|
|
25
|
-
'9xl': 'text-9xl',
|
|
3
|
+
xs: 'text-xs',
|
|
4
|
+
sm: 'text-sm',
|
|
5
|
+
base: 'text-base',
|
|
6
|
+
lg: 'text-lg',
|
|
7
|
+
xl: 'text-xl',
|
|
8
|
+
'2xl': 'text-2xl',
|
|
9
|
+
'3xl': 'text-3xl',
|
|
10
|
+
'4xl': 'text-4xl',
|
|
11
|
+
'5xl': 'text-5xl',
|
|
12
|
+
'6xl': 'text-6xl',
|
|
13
|
+
'7xl': 'text-7xl',
|
|
14
|
+
'8xl': 'text-8xl',
|
|
15
|
+
'9xl': 'text-9xl',
|
|
26
16
|
} as const;
|
|
27
17
|
|
|
28
|
-
/**
|
|
29
|
-
* Type for text size keys
|
|
30
|
-
*/
|
|
31
18
|
export type TextSizeKey = keyof typeof TEXT_SIZES;
|
|
32
19
|
|
|
33
|
-
/**
|
|
34
|
-
* Type for text size values
|
|
35
|
-
*/
|
|
36
20
|
export type TextSize = typeof TEXT_SIZES[TextSizeKey];
|
|
37
21
|
|
|
38
|
-
/**
|
|
39
|
-
* Font weight utilities
|
|
40
|
-
*/
|
|
41
22
|
export const FONT_WEIGHTS = {
|
|
42
|
-
thin: 'font-thin',
|
|
43
|
-
extralight: 'font-extralight',
|
|
44
|
-
light: 'font-light',
|
|
45
|
-
normal: 'font-normal',
|
|
46
|
-
medium: 'font-medium',
|
|
47
|
-
semibold: 'font-semibold',
|
|
48
|
-
bold: 'font-bold',
|
|
49
|
-
extrabold: 'font-extrabold',
|
|
50
|
-
black: 'font-black',
|
|
23
|
+
thin: 'font-thin',
|
|
24
|
+
extralight: 'font-extralight',
|
|
25
|
+
light: 'font-light',
|
|
26
|
+
normal: 'font-normal',
|
|
27
|
+
medium: 'font-medium',
|
|
28
|
+
semibold: 'font-semibold',
|
|
29
|
+
bold: 'font-bold',
|
|
30
|
+
extrabold: 'font-extrabold',
|
|
31
|
+
black: 'font-black',
|
|
51
32
|
} as const;
|
|
52
33
|
|
|
53
|
-
/**
|
|
54
|
-
* Type for font weight keys
|
|
55
|
-
*/
|
|
56
34
|
export type FontWeightKey = keyof typeof FONT_WEIGHTS;
|
|
57
35
|
|
|
58
|
-
/**
|
|
59
|
-
* Type for font weight values
|
|
60
|
-
*/
|
|
61
36
|
export type FontWeight = typeof FONT_WEIGHTS[FontWeightKey];
|
|
62
37
|
|
|
63
|
-
/**
|
|
64
|
-
* Common text sizes for different element types
|
|
65
|
-
*/
|
|
66
38
|
export const COMMON_TEXT_SIZES = {
|
|
67
39
|
caption: TEXT_SIZES.xs,
|
|
68
40
|
small: TEXT_SIZES.sm,
|
|
@@ -73,10 +45,6 @@ export const COMMON_TEXT_SIZES = {
|
|
|
73
45
|
display: TEXT_SIZES['4xl'],
|
|
74
46
|
} as const;
|
|
75
47
|
|
|
76
|
-
/**
|
|
77
|
-
* Text sizes by size variant
|
|
78
|
-
* Maps size variants to appropriate text sizes
|
|
79
|
-
*/
|
|
80
48
|
export const TEXT_SIZE_BY_VARIANT = {
|
|
81
49
|
xs: {
|
|
82
50
|
mobile: TEXT_SIZES.xs,
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Calculation Utils
|
|
3
|
-
* @description Small numeric helpers used by presentation components
|
|
4
|
-
* (progress percentage, pagination bounds, trend formatting, clamping)
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
|
-
// --- Clamping ---
|
|
8
2
|
|
|
9
3
|
export const clampToRange = (value: number, min: number, max: number): number => {
|
|
10
4
|
if (Number.isNaN(value)) return min;
|
|
@@ -20,7 +14,6 @@ export const clampToNonNegative = (value: number): number =>
|
|
|
20
14
|
export const clampToPositive = (value: number, fallback: number = 1): number =>
|
|
21
15
|
value <= 0 || Number.isNaN(value) ? fallback : value;
|
|
22
16
|
|
|
23
|
-
// --- Percentage ---
|
|
24
17
|
|
|
25
18
|
const PERCENT_MIN = 0;
|
|
26
19
|
const PERCENT_MAX = 100;
|
|
@@ -30,7 +23,6 @@ export const percentOfMax = (value: number, max: number): number => {
|
|
|
30
23
|
return clampToRange((value / max) * PERCENT_MAX, PERCENT_MIN, PERCENT_MAX);
|
|
31
24
|
};
|
|
32
25
|
|
|
33
|
-
// --- Trend formatting ---
|
|
34
26
|
|
|
35
27
|
const PERCENT_FRACTION_DIGITS = 1;
|
|
36
28
|
const SIGN_THRESHOLD = 0;
|
|
@@ -43,7 +35,6 @@ export const formatTrendMagnitude = (value: number, fractionDigits: number = PER
|
|
|
43
35
|
return `${sign}${Math.abs(value).toFixed(fractionDigits)}%`;
|
|
44
36
|
};
|
|
45
37
|
|
|
46
|
-
// --- Pagination bounds ---
|
|
47
38
|
|
|
48
39
|
export interface PageBounds {
|
|
49
40
|
totalPages: number;
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cn Utility
|
|
3
|
-
* @description Conditional className utility using clsx + tailwind-merge for proper Tailwind class merging with LRU cache for performance
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { clsx, type ClassValue } from 'clsx';
|
|
7
3
|
import { twMerge } from 'tailwind-merge';
|
|
8
4
|
|
|
9
5
|
export type { ClassValue };
|
|
10
6
|
|
|
11
|
-
// Simple LRU Cache implementation for className caching
|
|
12
7
|
class LRUCache<K, V> {
|
|
13
8
|
private cache: Map<K, V>;
|
|
14
9
|
private maxSize: number;
|
|
@@ -21,7 +16,6 @@ class LRUCache<K, V> {
|
|
|
21
16
|
get(key: K): V | undefined {
|
|
22
17
|
const value = this.cache.get(key);
|
|
23
18
|
if (value !== undefined) {
|
|
24
|
-
// Move to end (most recently used)
|
|
25
19
|
this.cache.delete(key);
|
|
26
20
|
this.cache.set(key, value);
|
|
27
21
|
}
|
|
@@ -29,14 +23,11 @@ class LRUCache<K, V> {
|
|
|
29
23
|
}
|
|
30
24
|
|
|
31
25
|
set(key: K, value: V): void {
|
|
32
|
-
// Remove existing entry to update position
|
|
33
26
|
if (this.cache.has(key)) {
|
|
34
27
|
this.cache.delete(key);
|
|
35
28
|
}
|
|
36
|
-
// Remove oldest entry if at capacity
|
|
37
29
|
else if (this.cache.size >= this.maxSize) {
|
|
38
30
|
const firstKey = this.cache.keys().next().value;
|
|
39
|
-
// FIX: Check if firstKey exists before deleting
|
|
40
31
|
if (firstKey !== undefined) {
|
|
41
32
|
this.cache.delete(firstKey);
|
|
42
33
|
}
|
|
@@ -49,23 +40,18 @@ class LRUCache<K, V> {
|
|
|
49
40
|
}
|
|
50
41
|
}
|
|
51
42
|
|
|
52
|
-
// Create cache instance
|
|
53
43
|
const classNameCache = new LRUCache<string, string>(256);
|
|
54
44
|
|
|
55
|
-
// FIX: Better cache key generator that handles functions and undefined
|
|
56
45
|
function generateCacheKey(inputs: ClassValue[]): string {
|
|
57
46
|
try {
|
|
58
|
-
// Convert inputs to a cacheable string representation
|
|
59
47
|
const normalized = inputs.map((input) => {
|
|
60
48
|
if (input === null || input === undefined) {
|
|
61
49
|
return 'null';
|
|
62
50
|
}
|
|
63
51
|
if (typeof input === 'function') {
|
|
64
|
-
// For functions, use a placeholder since functions can't be reliably serialized
|
|
65
52
|
return 'function';
|
|
66
53
|
}
|
|
67
54
|
if (typeof input === 'object') {
|
|
68
|
-
// Handle objects (like conditional objects in clsx)
|
|
69
55
|
return JSON.stringify(
|
|
70
56
|
Object.entries(input)
|
|
71
57
|
.filter(([, value]) => Boolean(value))
|
|
@@ -78,13 +64,11 @@ function generateCacheKey(inputs: ClassValue[]): string {
|
|
|
78
64
|
|
|
79
65
|
return normalized.join('|');
|
|
80
66
|
} catch {
|
|
81
|
-
// Fallback if anything goes wrong with serialization
|
|
82
67
|
return inputs.join('|');
|
|
83
68
|
}
|
|
84
69
|
}
|
|
85
70
|
|
|
86
71
|
export function cn(...inputs: ClassValue[]): string {
|
|
87
|
-
// Skip cache for dynamic inputs that might contain functions
|
|
88
72
|
const hasFunction = inputs.some((input) => typeof input === 'function');
|
|
89
73
|
|
|
90
74
|
if (!hasFunction) {
|
|
@@ -101,9 +85,7 @@ export function cn(...inputs: ClassValue[]): string {
|
|
|
101
85
|
return result;
|
|
102
86
|
}
|
|
103
87
|
|
|
104
|
-
// For inputs with functions, skip cache and compute directly
|
|
105
88
|
return twMerge(clsx(inputs));
|
|
106
89
|
}
|
|
107
90
|
|
|
108
|
-
// Export cache for manual clearing if needed
|
|
109
91
|
export { classNameCache };
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Money Utils
|
|
3
|
-
* @description Minor-unit money math and locale-aware formatting (ISO 4217)
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
|
-
/** Currencies that don't use 2 decimal places. Everything else defaults to 2. */
|
|
7
2
|
const FRACTION_DIGITS: Record<string, number> = {
|
|
8
3
|
BHD: 3,
|
|
9
4
|
IQD: 3,
|
|
@@ -58,7 +53,6 @@ export function toMinor(amount: number, currency: string): number {
|
|
|
58
53
|
return Math.round(amount * 10 ** currencyFractionDigits(currency));
|
|
59
54
|
}
|
|
60
55
|
|
|
61
|
-
/** Parses user input like '12.50' into minor units for the currency. Null when invalid. */
|
|
62
56
|
export function parseAmountToMinor(input: string, currency: string): number | null {
|
|
63
57
|
const digits = currencyFractionDigits(currency);
|
|
64
58
|
const trimmed = input.trim().replace(',', '.');
|
|
@@ -69,7 +63,6 @@ export function parseAmountToMinor(input: string, currency: string): number | nu
|
|
|
69
63
|
return Number.isSafeInteger(minor) ? minor : null;
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
/** Percentage of an amount, rounded half-up to whole minor units. */
|
|
73
66
|
export function pctOfMinor(amountMinor: number, pct: number): number {
|
|
74
67
|
return Math.round((amountMinor * pct) / 100);
|
|
75
68
|
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Responsive Utility Functions
|
|
3
|
-
* @description Centralized functions for building responsive Tailwind classes
|
|
4
|
-
* All responsive class construction logic lives here - components should only call these functions
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
2
|
import { cn } from './cn.util';
|
|
8
3
|
import type { SizeVariant, Breakpoint } from '../../domain/types';
|
|
@@ -13,25 +8,14 @@ import {
|
|
|
13
8
|
RESPONSIVE_TEXT_SIZE,
|
|
14
9
|
} from '../../domain/tokens/responsive-map.tokens';
|
|
15
10
|
|
|
16
|
-
/**
|
|
17
|
-
* Build responsive padding/margin/gap classes
|
|
18
|
-
* @param property - Tailwind spacing property ('p', 'm', 'px', 'py', 'gap', 'space-y', 'space-x')
|
|
19
|
-
* @param size - Size variant (sm, md, lg, xl)
|
|
20
|
-
* @returns Complete responsive class string
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* getSpacing('p', 'md') // "p-4 sm:p-5 md:p-5 lg:p-6 xl:p-8 2xl:p-10"
|
|
24
|
-
*/
|
|
25
11
|
export function getSpacing(
|
|
26
12
|
property: 'p' | 'm' | 'px' | 'py' | 'pt' | 'pb' | 'pl' | 'pr' | 'gap' | 'space-y' | 'space-x',
|
|
27
13
|
size: SizeVariant
|
|
28
14
|
): string {
|
|
29
15
|
const token = RESPONSIVE_SPACING[size];
|
|
30
16
|
|
|
31
|
-
// Start with base (xs/mobile) value
|
|
32
17
|
const classes: string[] = [`${property}-${token.xs}`];
|
|
33
18
|
|
|
34
|
-
// Add breakpoint-specific classes
|
|
35
19
|
const breakpoints: Breakpoint[] = ['sm', 'md', 'lg', 'xl', '2xl'];
|
|
36
20
|
breakpoints.forEach((bp) => {
|
|
37
21
|
if (token[bp] !== token.xs) {
|
|
@@ -42,14 +26,6 @@ export function getSpacing(
|
|
|
42
26
|
return classes.join(' ');
|
|
43
27
|
}
|
|
44
28
|
|
|
45
|
-
/**
|
|
46
|
-
* Build responsive icon size classes (height + width)
|
|
47
|
-
* @param size - Size variant
|
|
48
|
-
* @returns Combined height and width classes
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* getIconSize('md') // "h-5 w-5 sm:h-6 sm:w-6 md:h-7 md:w-7 lg:h-8 lg:w-8 xl:h-9 xl:w-9 2xl:h-10 2xl:w-10"
|
|
52
|
-
*/
|
|
53
29
|
export function getIconSize(size: SizeVariant): string {
|
|
54
30
|
const token = RESPONSIVE_ICON_SIZE[size];
|
|
55
31
|
const classes: string[] = [`h-${token.xs}`, `w-${token.xs}`];
|
|
@@ -64,14 +40,6 @@ export function getIconSize(size: SizeVariant): string {
|
|
|
64
40
|
return classes.join(' ');
|
|
65
41
|
}
|
|
66
42
|
|
|
67
|
-
/**
|
|
68
|
-
* Build responsive container size classes (for icon wrappers)
|
|
69
|
-
* @param size - Size variant
|
|
70
|
-
* @returns Combined height, width, and flex center classes
|
|
71
|
-
*
|
|
72
|
-
* @example
|
|
73
|
-
* getContainerSize('md') // "w-10 h-10 sm:w-12 sm:h-12 md:w-12 md:h-12..."
|
|
74
|
-
*/
|
|
75
43
|
export function getContainerSize(size: SizeVariant): string {
|
|
76
44
|
const token = RESPONSIVE_CONTAINER_SIZE[size];
|
|
77
45
|
const classes: string[] = [`w-${token.xs}`, `h-${token.xs}`];
|
|
@@ -86,53 +54,19 @@ export function getContainerSize(size: SizeVariant): string {
|
|
|
86
54
|
return classes.join(' ');
|
|
87
55
|
}
|
|
88
56
|
|
|
89
|
-
/**
|
|
90
|
-
* Get responsive text size classes
|
|
91
|
-
* @param size - Size variant
|
|
92
|
-
* @returns Text size class string
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* getTextSize('md') // "text-sm sm:text-base"
|
|
96
|
-
*/
|
|
97
57
|
export function getTextSize(size: SizeVariant): string {
|
|
98
58
|
const token = RESPONSIVE_TEXT_SIZE[size];
|
|
99
59
|
return cn(token.mobile, token.tablet);
|
|
100
60
|
}
|
|
101
61
|
|
|
102
|
-
/**
|
|
103
|
-
* Build responsive gap/spacing classes for layout
|
|
104
|
-
* @param size - Size variant
|
|
105
|
-
* @returns Gap class string
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* getGap('md') // "gap-3 sm:gap-4 md:gap-4 lg:gap-5 xl:gap-5 2xl:gap-6"
|
|
109
|
-
*/
|
|
110
62
|
export function getGap(size: SizeVariant): string {
|
|
111
63
|
return getSpacing('gap', size);
|
|
112
64
|
}
|
|
113
65
|
|
|
114
|
-
/**
|
|
115
|
-
* Build responsive space-y classes for vertical stacking
|
|
116
|
-
* @param size - Size variant
|
|
117
|
-
* @returns Space-y class string
|
|
118
|
-
*
|
|
119
|
-
* @example
|
|
120
|
-
* getSpaceY('md') // "space-y-3 sm:space-y-4 md:space-y-4 lg:space-y-5..."
|
|
121
|
-
*/
|
|
122
66
|
export function getSpaceY(size: SizeVariant): string {
|
|
123
67
|
return getSpacing('space-y', size);
|
|
124
68
|
}
|
|
125
69
|
|
|
126
|
-
/**
|
|
127
|
-
* Get complete responsive styles for common component patterns
|
|
128
|
-
* @param size - Size variant
|
|
129
|
-
* @param options - Which styles to include
|
|
130
|
-
* @returns Combined class string
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* getResponsiveStyles('md', { padding: true, gap: true })
|
|
134
|
-
* // "p-4 sm:p-5 md:p-5 lg:p-6 xl:p-8 2xl:p-10 gap-3 sm:gap-4..."
|
|
135
|
-
*/
|
|
136
70
|
export function getResponsiveStyles(
|
|
137
71
|
size: SizeVariant,
|
|
138
72
|
options: {
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AspectRatio Component
|
|
3
|
-
* @description Aspect ratio container component
|
|
4
|
-
* @example
|
|
5
|
-
* import { AspectRatio } from '@umituz/web-design-system/atoms';
|
|
6
|
-
*
|
|
7
|
-
* <AspectRatio ratio={16 / 9}>
|
|
8
|
-
* <img src="..." alt="..." className="object-cover w-full h-full" />
|
|
9
|
-
* </AspectRatio>
|
|
10
|
-
*/
|
|
11
1
|
|
|
12
2
|
import * as React from "react";
|
|
13
3
|
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Badge Component (Atom)
|
|
3
|
-
* @description Small status or label indicator
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';
|
|
7
3
|
import { cn } from '../../infrastructure/utils';
|
|
@@ -13,7 +9,6 @@ export type BadgeVariant = ColorVariant | 'info' | 'danger' | 'neutral' | 'accen
|
|
|
13
9
|
export interface BadgeProps extends HTMLAttributes<HTMLDivElement>, BaseProps {
|
|
14
10
|
variant?: BadgeVariant;
|
|
15
11
|
tone?: BadgeVariant;
|
|
16
|
-
/** Icon rendered before the label; svgs are sized to the badge text. */
|
|
17
12
|
icon?: ReactNode;
|
|
18
13
|
size?: MediumSizes;
|
|
19
14
|
small?: boolean;
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BatteryPill Component (Atom)
|
|
3
|
-
* @description Battery charge indicator: outline cell with fill level and percentage
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { forwardRef, type HTMLAttributes } from 'react';
|
|
7
3
|
import { cn } from '../../infrastructure/utils';
|
|
8
4
|
import type { BaseProps } from '../../domain/types';
|
|
9
5
|
|
|
10
6
|
export interface BatteryPillProps extends HTMLAttributes<HTMLSpanElement>, BaseProps {
|
|
11
|
-
/** Charge percentage, clamped to 0-100. */
|
|
12
7
|
pct: number;
|
|
13
8
|
}
|
|
14
9
|
|
|
@@ -46,7 +46,6 @@ export interface ButtonProps
|
|
|
46
46
|
icon?: React.ReactNode;
|
|
47
47
|
iconRight?: React.ReactNode;
|
|
48
48
|
loading?: boolean;
|
|
49
|
-
/** Renders the button as a client-side navigation link (`react-router`). */
|
|
50
49
|
to?: string;
|
|
51
50
|
}
|
|
52
51
|
|
|
@@ -60,7 +59,6 @@ const Button = React.memo(React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
60
59
|
className={cn(block && 'w-full', buttonVariants({ variant, size, className }))}
|
|
61
60
|
ref={ref as React.Ref<HTMLAnchorElement>}
|
|
62
61
|
aria-disabled={disabled || loading || undefined}
|
|
63
|
-
// Button-typed DOM props (onClick, aria-*, …) flow straight onto the anchor.
|
|
64
62
|
{...(props as unknown as React.AnchorHTMLAttributes<HTMLAnchorElement>)}
|
|
65
63
|
>
|
|
66
64
|
{loading ? <Spinner size="sm" /> : icon}
|