@umituz/web-design-system 5.3.0 → 5.3.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/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__/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 +0 -21
- 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/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/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 +0 -5
- 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/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/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/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/index.ts +0 -6
- 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/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/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 +0 -5
- 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/index.ts +0 -5
- 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,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Show Component (Atom)
|
|
3
|
-
* @description Conditionally render content based on screen breakpoint
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { useBreakpoint } from '../hooks/useMediaQuery';
|
|
7
3
|
import type { ShowProps } from '../../domain/types/breakpoint.types';
|
|
@@ -11,29 +7,9 @@ export interface ShowComponentProps extends BaseProps, ShowProps {
|
|
|
11
7
|
children: React.ReactNode;
|
|
12
8
|
}
|
|
13
9
|
|
|
14
|
-
/**
|
|
15
|
-
* Conditionally show content based on breakpoint
|
|
16
|
-
* @param above - Show on screens larger than this breakpoint
|
|
17
|
-
* @param below - Show on screens smaller than this breakpoint
|
|
18
|
-
* @param at - Show only on this specific breakpoint
|
|
19
|
-
* @param children - Content to show
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```tsx
|
|
23
|
-
* // Show on desktop (lg and above)
|
|
24
|
-
* <Show above="lg"><DesktopNav /></Show>
|
|
25
|
-
*
|
|
26
|
-
* // Show on mobile (sm and below)
|
|
27
|
-
* <Show below="md"><MobileNav /></Show>
|
|
28
|
-
*
|
|
29
|
-
* // Show only on tablet
|
|
30
|
-
* <Show at="md"><TabletContent /></Show>
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
10
|
export const Show = ({ above, below, at, children, className }: ShowComponentProps) => {
|
|
34
11
|
const { matches, isGreaterThan, isLessThan } = useBreakpoint();
|
|
35
12
|
|
|
36
|
-
// Determine if content should be shown
|
|
37
13
|
let shouldShow = true;
|
|
38
14
|
|
|
39
15
|
if (above) {
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useClickOutside Hook
|
|
3
|
-
* @description Detect clicks outside an element
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { useEffect, useRef } from 'react';
|
|
7
3
|
|
|
@@ -10,8 +6,6 @@ export function useClickOutside<T extends HTMLElement>(
|
|
|
10
6
|
enabled: boolean = true
|
|
11
7
|
) {
|
|
12
8
|
const ref = useRef<T>(null);
|
|
13
|
-
// Use a ref to keep the latest callback without retriggering the effect
|
|
14
|
-
// on every render — callers commonly pass inline functions.
|
|
15
9
|
const callbackRef = useRef(callback);
|
|
16
10
|
useEffect(() => {
|
|
17
11
|
callbackRef.current = callback;
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useDebounce Hook
|
|
3
|
-
* @description Debounce a value with optimized performance
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
7
3
|
import { TIMING } from '../../infrastructure/constants/timing.constants';
|
|
@@ -30,10 +26,6 @@ export function useDebounce<T>(value: T, delay: number = TIMING.DEBOUNCE_DEFAULT
|
|
|
30
26
|
return debouncedValue;
|
|
31
27
|
}
|
|
32
28
|
|
|
33
|
-
/**
|
|
34
|
-
* useThrottle Hook
|
|
35
|
-
* @description Throttle a function to limit execution rate
|
|
36
|
-
*/
|
|
37
29
|
export function useThrottle<T extends (...args: unknown[]) => unknown>(
|
|
38
30
|
func: T,
|
|
39
31
|
delay: number = TIMING.THROTTLE_DEFAULT_MS
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useKeyboard Hook
|
|
3
|
-
* @description Keyboard event handling
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { useEffect, useCallback } from 'react';
|
|
7
3
|
|
|
@@ -16,7 +12,6 @@ export interface KeyboardOptions {
|
|
|
16
12
|
enabled?: boolean;
|
|
17
13
|
}
|
|
18
14
|
|
|
19
|
-
// Helper function to check if modifiers match
|
|
20
15
|
const checkModifiers = (event: KeyboardEvent, modifiers: KeyboardModifier[]): boolean => {
|
|
21
16
|
return modifiers.every((mod) => {
|
|
22
17
|
switch (mod) {
|
|
@@ -41,7 +36,6 @@ export function useKeyboard({
|
|
|
41
36
|
onKeyUp,
|
|
42
37
|
enabled = true,
|
|
43
38
|
}: KeyboardOptions) {
|
|
44
|
-
// FIX: Create handlers once and reuse to prevent duplicate logic
|
|
45
39
|
const handleKeyDown = useCallback((event: KeyboardEvent) => {
|
|
46
40
|
const keyMatches = event.key.toLowerCase() === key.toLowerCase();
|
|
47
41
|
const modifiersMatch = checkModifiers(event, modifiers);
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useMediaQuery & useBreakpoint Hooks
|
|
3
|
-
* @description Enhanced responsive breakpoint detection with helper functions
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { useEffect, useState, useCallback, useRef, useMemo, useSyncExternalStore } from 'react';
|
|
7
3
|
import type {
|
|
@@ -16,16 +12,6 @@ import {
|
|
|
16
12
|
} from '../../infrastructure/constants/breakpoint.constants';
|
|
17
13
|
import { TIMING } from '../../infrastructure/constants/timing.constants';
|
|
18
14
|
|
|
19
|
-
/**
|
|
20
|
-
* Simple media query hook for a single breakpoint
|
|
21
|
-
* @param breakpoint - The breakpoint to check
|
|
22
|
-
* @returns Whether the media query matches
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```tsx
|
|
26
|
-
* const isDesktop = useMediaQuery('lg') // true on lg screens and above
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
15
|
export function useMediaQuery(breakpoint: Breakpoint): boolean {
|
|
30
16
|
const query = useMemo(() => createMediaQuery(breakpoint), [breakpoint]);
|
|
31
17
|
|
|
@@ -49,24 +35,7 @@ export function useMediaQuery(breakpoint: Breakpoint): boolean {
|
|
|
49
35
|
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
50
36
|
}
|
|
51
37
|
|
|
52
|
-
/**
|
|
53
|
-
* Enhanced breakpoint hook with helper functions
|
|
54
|
-
* @returns Object containing current breakpoint and helper functions
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```tsx
|
|
58
|
-
* const { currentBreakpoint, isMobile, isDesktop } = useBreakpoint()
|
|
59
|
-
*
|
|
60
|
-
* return (
|
|
61
|
-
* <div>
|
|
62
|
-
* {isMobile && <MobileNav />}
|
|
63
|
-
* {isDesktop && <DesktopNav />}
|
|
64
|
-
* </div>
|
|
65
|
-
* )
|
|
66
|
-
* ```
|
|
67
|
-
*/
|
|
68
38
|
export function useBreakpoint(): UseBreakpointReturn {
|
|
69
|
-
// FIX: Sort breakpoints once and memoize
|
|
70
39
|
const sortedBreakpoints = useMemo(() => {
|
|
71
40
|
return Object.entries(BREAKPOINTS).sort(
|
|
72
41
|
([, a], [, b]) => b.min - a.min
|
|
@@ -74,7 +43,6 @@ export function useBreakpoint(): UseBreakpointReturn {
|
|
|
74
43
|
}, []);
|
|
75
44
|
|
|
76
45
|
const [currentBreakpoint, setCurrentBreakpoint] = useState<Breakpoint>(() => {
|
|
77
|
-
// FIX: SSR-safe initialization
|
|
78
46
|
if (typeof window === 'undefined') return 'lg';
|
|
79
47
|
|
|
80
48
|
const width = window.innerWidth;
|
|
@@ -84,7 +52,6 @@ export function useBreakpoint(): UseBreakpointReturn {
|
|
|
84
52
|
return 'xs';
|
|
85
53
|
});
|
|
86
54
|
|
|
87
|
-
// FIX: Use useRef for resize timer to prevent timer issues
|
|
88
55
|
const resizeTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
|
89
56
|
|
|
90
57
|
useEffect(() => {
|
|
@@ -99,10 +66,8 @@ export function useBreakpoint(): UseBreakpointReturn {
|
|
|
99
66
|
}
|
|
100
67
|
};
|
|
101
68
|
|
|
102
|
-
// Initial check
|
|
103
69
|
updateBreakpoint();
|
|
104
70
|
|
|
105
|
-
// Debounced resize listener
|
|
106
71
|
const handleResize = () => {
|
|
107
72
|
if (resizeTimerRef.current) {
|
|
108
73
|
clearTimeout(resizeTimerRef.current);
|
|
@@ -120,7 +85,6 @@ export function useBreakpoint(): UseBreakpointReturn {
|
|
|
120
85
|
};
|
|
121
86
|
}, [sortedBreakpoints]);
|
|
122
87
|
|
|
123
|
-
// Helper functions
|
|
124
88
|
const matches = useCallback(
|
|
125
89
|
(breakpoint: Breakpoint | Breakpoint[]): boolean => {
|
|
126
90
|
if (Array.isArray(breakpoint)) {
|
|
@@ -145,7 +109,6 @@ export function useBreakpoint(): UseBreakpointReturn {
|
|
|
145
109
|
[currentBreakpoint]
|
|
146
110
|
);
|
|
147
111
|
|
|
148
|
-
// Computed values
|
|
149
112
|
const isXs = currentBreakpoint === 'xs';
|
|
150
113
|
const isSm = currentBreakpoint === 'sm';
|
|
151
114
|
const isMd = currentBreakpoint === 'md';
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useScrollLock Hook
|
|
3
|
-
* @description Lock/unlock body scroll while preserving scrollbar gutter
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { useEffect } from 'react';
|
|
7
3
|
|
|
8
|
-
/** Width of the vertical scrollbar, so locking scroll doesn't shift layout. */
|
|
9
4
|
const measureScrollbarWidth = (): number => {
|
|
10
5
|
if (typeof window === 'undefined' || typeof document === 'undefined') return 0;
|
|
11
6
|
return Math.max(0, window.innerWidth - document.documentElement.clientWidth);
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useTheme Hook
|
|
3
|
-
* @description Theme toggle functionality with SSR support and system preference detection
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { useEffect, useState, useCallback } from 'react';
|
|
7
3
|
|
|
@@ -25,9 +21,16 @@ const getSystemTheme = (): 'light' | 'dark' => {
|
|
|
25
21
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
26
22
|
};
|
|
27
23
|
|
|
24
|
+
const writeThemeStorage = (theme: Theme): void => {
|
|
25
|
+
try {
|
|
26
|
+
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
|
27
|
+
} catch {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
28
32
|
export function useTheme(): UseThemeReturn {
|
|
29
33
|
const [theme, setThemeState] = useState<Theme>(() => {
|
|
30
|
-
// FIX: SSR-safe initialization
|
|
31
34
|
if (typeof window === 'undefined') return 'system';
|
|
32
35
|
|
|
33
36
|
try {
|
|
@@ -47,7 +50,6 @@ export function useTheme(): UseThemeReturn {
|
|
|
47
50
|
return theme;
|
|
48
51
|
});
|
|
49
52
|
|
|
50
|
-
// Listen for system theme changes
|
|
51
53
|
useEffect(() => {
|
|
52
54
|
if (theme !== 'system') return;
|
|
53
55
|
|
|
@@ -66,15 +68,9 @@ export function useTheme(): UseThemeReturn {
|
|
|
66
68
|
|
|
67
69
|
root.classList.remove('light', 'dark');
|
|
68
70
|
root.classList.add(themeToApply);
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
|
72
|
-
} catch {
|
|
73
|
-
// Ignore storage errors
|
|
74
|
-
}
|
|
71
|
+
writeThemeStorage(theme);
|
|
75
72
|
}, [theme]);
|
|
76
73
|
|
|
77
|
-
// FIX: Memoize callbacks to prevent unnecessary re-renders
|
|
78
74
|
const toggleTheme = useCallback(() => {
|
|
79
75
|
setThemeState((prev) => {
|
|
80
76
|
if (prev === 'light') return 'dark';
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Avatar Component (Molecule)
|
|
3
|
-
* @description User avatar with image and fallback (shadcn/ui compatible)
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { forwardRef, type HTMLAttributes, type ComponentPropsWithoutRef } from 'react';
|
|
7
3
|
import { cn } from '../../infrastructure/utils';
|
|
@@ -11,7 +7,6 @@ import type { ExtendedSizes } from '../../infrastructure/constants';
|
|
|
11
7
|
export interface AvatarProps extends HTMLAttributes<HTMLDivElement>, BaseProps {
|
|
12
8
|
name?: string;
|
|
13
9
|
size?: ExtendedSizes;
|
|
14
|
-
/** Accent hue (0-360) for the initials fallback, overriding the name-hash tone. */
|
|
15
10
|
hue?: number;
|
|
16
11
|
}
|
|
17
12
|
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BookmarkButton Component (Molecule)
|
|
3
|
-
* @description Accessible toggleable button for saving, bookmarking, or favoriting items.
|
|
4
|
-
* Supports pill (icon + label) and icon-only variants, multiple sizes, semantic tones, and custom icons.
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
2
|
import React, { forwardRef } from 'react';
|
|
8
3
|
import { Star } from 'lucide-react';
|
|
@@ -15,25 +10,15 @@ export type BookmarkTone = 'warning' | 'primary' | 'success' | 'default';
|
|
|
15
10
|
export interface BookmarkButtonProps
|
|
16
11
|
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'onChange' | 'onToggle'>,
|
|
17
12
|
BaseProps {
|
|
18
|
-
/** Whether the item is currently saved / bookmarked / active. */
|
|
19
13
|
active?: boolean;
|
|
20
|
-
/** Callback fired when clicked or toggled. Passes the inverted active state. */
|
|
21
14
|
onToggle?: (nextActive: boolean) => void;
|
|
22
|
-
/** Display variant: 'pill' shows icon + label, 'icon' renders a square/round icon-only button. */
|
|
23
15
|
variant?: 'pill' | 'icon';
|
|
24
|
-
/** Size variant */
|
|
25
16
|
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
26
|
-
/** Inactive label (default: "Save"). Used as text in pill mode and aria-label in icon mode. */
|
|
27
17
|
label?: string;
|
|
28
|
-
/** Active label (default: "Saved"). */
|
|
29
18
|
activeLabel?: string;
|
|
30
|
-
/** Custom icon when inactive. Defaults to a Star outline. */
|
|
31
19
|
icon?: React.ReactNode;
|
|
32
|
-
/** Custom icon when active. Defaults to a filled Star. */
|
|
33
20
|
activeIcon?: React.ReactNode;
|
|
34
|
-
/** Color theme tone when active (default: 'warning' for warm bookmark gold). */
|
|
35
21
|
tone?: BookmarkTone;
|
|
36
|
-
/** Loading indicator */
|
|
37
22
|
loading?: boolean;
|
|
38
23
|
}
|
|
39
24
|
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Chip Component (Molecule)
|
|
3
|
-
* @description Selectable/removable tag
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { forwardRef } from 'react';
|
|
7
3
|
import { cn } from '../../infrastructure/utils';
|
|
@@ -15,7 +11,6 @@ export interface ChipProps extends BaseProps {
|
|
|
15
11
|
onRemove?: () => void;
|
|
16
12
|
onClick?: () => void;
|
|
17
13
|
removable?: boolean;
|
|
18
|
-
/** Toggle state for filter chips — renders as a pressed button when set. */
|
|
19
14
|
selected?: boolean;
|
|
20
15
|
}
|
|
21
16
|
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CodeBlock Component (Molecule)
|
|
3
|
-
* @description Syntax-highlighted code block with copy button and expand/collapse
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { useState, useEffect } from 'react';
|
|
7
3
|
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
@@ -15,7 +11,6 @@ export interface CodeBlockProps extends BaseProps {
|
|
|
15
11
|
language?: string;
|
|
16
12
|
filename?: string;
|
|
17
13
|
showLineNumbers?: boolean;
|
|
18
|
-
/** Lines threshold to enable collapse/expand. */
|
|
19
14
|
collapseThreshold?: number;
|
|
20
15
|
}
|
|
21
16
|
|
|
@@ -13,18 +13,11 @@ export interface FieldProps {
|
|
|
13
13
|
error?: ReactNode;
|
|
14
14
|
required?: boolean;
|
|
15
15
|
htmlFor?: string;
|
|
16
|
-
/** Keep the label in the accessibility tree while hiding it visually. */
|
|
17
16
|
hideLabel?: boolean;
|
|
18
17
|
className?: string;
|
|
19
18
|
children: ReactNode;
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
/**
|
|
23
|
-
* Label + hint/error wrapper for a single form control. When the child is a
|
|
24
|
-
* valid element, the control id (existing id wins, then htmlFor, then a
|
|
25
|
-
* generated one) and the aria wiring for hint/error are injected into it so
|
|
26
|
-
* the label association is automatic.
|
|
27
|
-
*/
|
|
28
21
|
export function Field({ label, hint, error, required, htmlFor, hideLabel = false, className, children }: FieldProps) {
|
|
29
22
|
const autoId = useId();
|
|
30
23
|
const childId = isValidElement(children) ? (children as ReactElement<FieldControlProps>).props.id : undefined;
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FormField Component (Molecule)
|
|
3
|
-
* @description Label + Input combination (Responsive)
|
|
4
|
-
*/
|
|
5
1
|
|
|
6
2
|
import { forwardRef, useId } from 'react';
|
|
7
3
|
import { cn, getSpaceY } from '../../infrastructure/utils';
|
|
@@ -16,7 +12,7 @@ export interface FormFieldProps extends BaseProps {
|
|
|
16
12
|
required?: boolean;
|
|
17
13
|
id?: string;
|
|
18
14
|
inputProps?: React.ComponentProps<typeof Input>;
|
|
19
|
-
fullWidth?: boolean;
|
|
15
|
+
fullWidth?: boolean;
|
|
20
16
|
}
|
|
21
17
|
|
|
22
18
|
export const FormField = forwardRef<HTMLInputElement, FormFieldProps>(
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OptionTile Component (Molecule)
|
|
3
|
-
* @description Graded answer tile for exercises — a native input (radio or
|
|
4
|
-
* checkbox) wrapped in its label, with an optional A/B/C/D key badge and
|
|
5
|
-
* reveal states (correct / wrong / missed) for post-submission feedback.
|
|
6
|
-
*/
|
|
7
1
|
|
|
8
2
|
import { forwardRef } from 'react';
|
|
9
3
|
import { Check, X } from 'lucide-react';
|
|
@@ -14,15 +8,11 @@ export type OptionTileState = 'correct' | 'wrong' | 'missed';
|
|
|
14
8
|
|
|
15
9
|
export interface OptionTileProps extends BaseProps {
|
|
16
10
|
label: React.ReactNode;
|
|
17
|
-
/** Key badge rendered before the label (A, B, C…). */
|
|
18
11
|
keyBadge?: string;
|
|
19
|
-
/** Native input type — radio (single) or checkbox (multi-select). */
|
|
20
12
|
type?: 'radio' | 'checkbox';
|
|
21
13
|
checked?: boolean;
|
|
22
|
-
/** Reveal semantics — overrides selection styling after grading. */
|
|
23
14
|
state?: OptionTileState;
|
|
24
15
|
disabled?: boolean;
|
|
25
|
-
/** Group name for radio inputs. */
|
|
26
16
|
name?: string;
|
|
27
17
|
onChange: (checked: boolean) => void;
|
|
28
18
|
}
|
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RatingStars Component (Molecule)
|
|
3
|
-
* @description 0–5 rating with visual half-star precision, numeric value and
|
|
4
|
-
* optional review count
|
|
5
|
-
*/
|
|
6
1
|
|
|
7
2
|
import { cn } from '../../infrastructure/utils';
|
|
8
3
|
import type { BaseProps } from '../../domain/types';
|
|
9
4
|
|
|
10
5
|
export interface RatingStarsProps extends BaseProps {
|
|
11
|
-
/** 0–5 rating; halves render as partial star fills. */
|
|
12
6
|
value: number;
|
|
13
|
-
/** Optional review count rendered in parentheses. */
|
|
14
7
|
count?: number;
|
|
15
|
-
/** Locale-formatted count override (e.g. 1,204). */
|
|
16
8
|
formatCount?: (count: number) => string;
|
|
17
9
|
size?: 'sm' | 'md';
|
|
18
10
|
}
|
|
@@ -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
|
|