doom-design-system 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +5 -2
  2. package/dist/components/Accordion/Accordion.d.ts +17 -0
  3. package/dist/components/Accordion/Accordion.js +94 -0
  4. package/dist/components/Accordion/index.d.ts +1 -0
  5. package/dist/components/Accordion/index.js +1 -0
  6. package/dist/components/Alert/Alert.d.ts +11 -0
  7. package/dist/components/Alert/Alert.js +70 -0
  8. package/dist/components/Alert/index.d.ts +1 -0
  9. package/dist/components/Alert/index.js +1 -0
  10. package/dist/components/Avatar/Avatar.d.ts +13 -0
  11. package/dist/components/Avatar/Avatar.js +51 -0
  12. package/dist/components/Avatar/index.d.ts +1 -0
  13. package/dist/components/Avatar/index.js +1 -0
  14. package/dist/components/Badge/Badge.js +1 -1
  15. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +13 -0
  16. package/dist/components/Breadcrumbs/Breadcrumbs.js +46 -0
  17. package/dist/components/Breadcrumbs/index.d.ts +1 -0
  18. package/dist/components/Breadcrumbs/index.js +1 -0
  19. package/dist/components/Button/Button.js +39 -6
  20. package/dist/components/Card/Card.js +1 -1
  21. package/dist/components/Drawer/Drawer.d.ts +12 -0
  22. package/dist/components/Drawer/Drawer.js +101 -0
  23. package/dist/components/Drawer/index.d.ts +1 -0
  24. package/dist/components/Drawer/index.js +1 -0
  25. package/dist/components/Form/Form.d.ts +20 -7
  26. package/dist/components/Form/Form.js +49 -9
  27. package/dist/components/Input/Input.d.ts +1 -1
  28. package/dist/components/Input/Input.js +27 -60
  29. package/dist/components/Label/Label.d.ts +6 -0
  30. package/dist/components/Label/Label.js +38 -0
  31. package/dist/components/Label/index.d.ts +1 -0
  32. package/dist/components/Label/index.js +1 -0
  33. package/dist/components/Link/Link.js +1 -1
  34. package/dist/components/Modal/Modal.js +7 -7
  35. package/dist/components/Pagination/Pagination.d.ts +8 -0
  36. package/dist/components/Pagination/Pagination.js +94 -0
  37. package/dist/components/Pagination/index.d.ts +1 -0
  38. package/dist/components/Pagination/index.js +1 -0
  39. package/dist/components/Popover/Popover.d.ts +1 -1
  40. package/dist/components/Popover/Popover.js +47 -29
  41. package/dist/components/RadioGroup/RadioGroup.d.ts +19 -0
  42. package/dist/components/RadioGroup/RadioGroup.js +101 -0
  43. package/dist/components/RadioGroup/index.d.ts +1 -0
  44. package/dist/components/RadioGroup/index.js +1 -0
  45. package/dist/components/Select/Select.js +5 -17
  46. package/dist/components/Sheet/Sheet.d.ts +10 -0
  47. package/dist/components/Sheet/Sheet.js +87 -0
  48. package/dist/components/Sheet/index.d.ts +1 -0
  49. package/dist/components/Sheet/index.js +1 -0
  50. package/dist/components/Slider/Slider.d.ts +10 -0
  51. package/dist/components/Slider/Slider.js +168 -0
  52. package/dist/components/Slider/index.d.ts +1 -0
  53. package/dist/components/Slider/index.js +1 -0
  54. package/dist/components/Switch/Switch.d.ts +11 -0
  55. package/dist/components/Switch/Switch.js +77 -0
  56. package/dist/components/Switch/index.d.ts +1 -0
  57. package/dist/components/Switch/index.js +1 -0
  58. package/dist/components/Table/Table.js +6 -6
  59. package/dist/components/Tabs/Tabs.js +6 -9
  60. package/dist/components/Textarea/Textarea.js +3 -10
  61. package/dist/components/Toast/Toast.js +1 -1
  62. package/dist/components/Tooltip/Tooltip.d.ts +9 -0
  63. package/dist/components/Tooltip/Tooltip.js +37 -0
  64. package/dist/components/Tooltip/index.d.ts +1 -0
  65. package/dist/components/Tooltip/index.js +1 -0
  66. package/dist/index.d.ts +12 -0
  67. package/dist/index.js +12 -0
  68. package/dist/styles/mixins.d.ts +3 -0
  69. package/dist/styles/mixins.js +25 -0
  70. package/dist/styles/themes/definitions.d.ts +112 -0
  71. package/dist/styles/themes/definitions.js +37 -4
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  var __rest = (this && this.__rest) || function (s, e) {
2
3
  var t = {};
3
4
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
@@ -9,16 +10,55 @@ var __rest = (this && this.__rest) || function (s, e) {
9
10
  }
10
11
  return t;
11
12
  };
12
- import { jsx as _jsx } from "react/jsx-runtime";
13
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ import styled from '@emotion/styled';
15
+ import { css } from '@emotion/react';
16
+ const StyledForm = styled.form `
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: 1.5rem;
20
+ width: 100%;
21
+ `;
22
+ const StyledField = styled.div `
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: 0.5rem;
26
+ width: 100%;
27
+
28
+ `;
29
+ import { Label } from '../Label/Label';
30
+ export { Label };
31
+ const StyledMessage = styled.span `
32
+ font-size: 0.75rem;
33
+ font-weight: 500;
34
+
35
+ ${props => props.variant === 'error' && css `
36
+ color: var(--error);
37
+ font-weight: 700;
38
+ text-transform: uppercase;
39
+ animation: shake 0.3s ease-in-out;
40
+ `}
41
+
42
+ ${props => props.variant === 'description' && css `
43
+ color: var(--muted-foreground);
44
+ `}
45
+
46
+ @keyframes shake {
47
+ 0%, 100% { transform: translateX(0); }
48
+ 25% { transform: translateX(-4px); }
49
+ 75% { transform: translateX(4px); }
50
+ }
51
+ `;
13
52
  export function Form(_a) {
14
- var { children, className = '', style } = _a, props = __rest(_a, ["children", "className", "style"]);
15
- return (_jsx("form", Object.assign({ className: className, style: Object.assign({ display: 'flex', flexDirection: 'column', gap: '1.5rem' }, style) }, props, { children: children })));
53
+ var { children } = _a, props = __rest(_a, ["children"]);
54
+ return _jsx(StyledForm, Object.assign({}, props, { children: children }));
16
55
  }
17
- export function FormGroup(_a) {
18
- var { children, className = '', style } = _a, props = __rest(_a, ["children", "className", "style"]);
19
- return (_jsx("div", Object.assign({ className: className, style: Object.assign({ display: 'flex', flexDirection: 'column', gap: '0.5rem' }, style) }, props, { children: children })));
56
+ export function FormMessage(_a) {
57
+ var { children, variant = 'description' } = _a, props = __rest(_a, ["children", "variant"]);
58
+ return _jsx(StyledMessage, Object.assign({ variant: variant }, props, { children: children }));
20
59
  }
21
- export function Label(_a) {
22
- var { children, className = '', style } = _a, props = __rest(_a, ["children", "className", "style"]);
23
- return (_jsx("label", Object.assign({ className: className, style: Object.assign({ fontWeight: 700, fontSize: '0.9rem', color: '#000000', textTransform: 'uppercase', letterSpacing: '0.05em' }, style) }, props, { children: children })));
60
+ export function Field(_a) {
61
+ var { children, label, error, description, htmlFor, required } = _a, props = __rest(_a, ["children", "label", "error", "description", "htmlFor", "required"]);
62
+ return (_jsxs(StyledField, Object.assign({ error: !!error }, props, { children: [label && (_jsx(Label, { htmlFor: htmlFor, required: required, children: label })), children, description && !error && (_jsx(FormMessage, { variant: "description", children: description })), error && typeof error === 'string' && (_jsx(FormMessage, { variant: "error", children: error }))] })));
24
63
  }
64
+ export const FormGroup = StyledField;
@@ -8,5 +8,5 @@ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
8
8
  format?: (value: string | number | readonly string[] | undefined) => string;
9
9
  validate?: (value: string | number | readonly string[] | undefined) => string | undefined;
10
10
  }
11
- export declare function Input({ label, error: errorProp, helperText, startAdornment, endAdornment, style, className, format, validate, onBlur, onFocus, value, ...props }: InputProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function Input({ label, error: errorProp, helperText, startAdornment, endAdornment, style, className, format, validate, onBlur, onFocus, value, id, required, ...props }: InputProps): import("react/jsx-runtime").JSX.Element;
12
12
  export {};
@@ -13,10 +13,13 @@ var __rest = (this && this.__rest) || function (s, e) {
13
13
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
14
  import { useState } from 'react';
15
15
  import styled from '@emotion/styled';
16
- import { Text } from '../..';
16
+ import { css } from '@emotion/react';
17
+ import { Label } from '../Label/Label';
18
+ import { baseInteractiveStyles, focusStyles, errorStyles } from '../../styles/mixins';
17
19
  const InputContainer = styled.div `
18
20
  display: flex;
19
21
  flex-direction: column;
22
+ gap: 0.5rem;
20
23
  width: 100%;
21
24
  `;
22
25
  const InputWrapper = styled.div `
@@ -33,67 +36,15 @@ const StyledInput = styled.input `
33
36
  font-size: var(--text-base);
34
37
  background: var(--card-bg);
35
38
  color: var(--foreground);
36
- border: var(--border-width) solid ${props => props.isError ? 'var(--error)' : 'var(--card-border)'};
37
- border-radius: var(--radius);
38
- box-shadow: var(--shadow-hard);
39
- outline: none;
40
- transition: all 0.1s ease;
41
-
42
- &:focus {
43
- box-shadow: var(--shadow-hover);
44
- transform: translate(-2px, -2px);
45
- border-color: ${props => props.isError ? 'var(--error)' : 'var(--primary)'};
46
- }
39
+
40
+ ${baseInteractiveStyles}
41
+ ${focusStyles}
47
42
 
48
43
  &::placeholder {
49
44
  color: var(--muted);
50
45
  }
51
46
 
52
- &::-webkit-calendar-picker-indicator {
53
- background-color: var(--muted-foreground);
54
- cursor: pointer;
55
- mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");
56
- -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");
57
- mask-repeat: no-repeat;
58
- -webkit-mask-repeat: no-repeat;
59
- mask-position: center;
60
- -webkit-mask-position: center;
61
- mask-size: contain;
62
- -webkit-mask-size: contain;
63
- transition: background-color 0.2s;
64
- width: 1em;
65
- height: 1em;
66
-
67
- &:hover {
68
- background-color: var(--foreground);
69
- }
70
- }
71
-
72
- &[type="time"]::-webkit-calendar-picker-indicator {
73
- mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");
74
- -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");
75
- }
76
-
77
- &::-webkit-search-cancel-button {
78
- -webkit-appearance: none;
79
- background-color: var(--muted-foreground);
80
- cursor: pointer;
81
- mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");
82
- -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");
83
- mask-repeat: no-repeat;
84
- -webkit-mask-repeat: no-repeat;
85
- mask-position: center;
86
- -webkit-mask-position: center;
87
- mask-size: contain;
88
- -webkit-mask-size: contain;
89
- transition: background-color 0.2s;
90
- width: 1em;
91
- height: 1em;
92
-
93
- &:hover {
94
- background-color: var(--foreground);
95
- }
96
- }
47
+ ${props => props.isError && errorStyles}
97
48
  `;
98
49
  const Adornment = styled.span `
99
50
  position: absolute;
@@ -103,8 +54,25 @@ const Adornment = styled.span `
103
54
  pointer-events: none;
104
55
  z-index: 1;
105
56
  `;
57
+ const HelperText = styled.span `
58
+ font-size: var(--text-xs);
59
+ font-weight: var(--font-medium);
60
+ color: ${props => props.isError ? 'var(--error)' : 'var(--muted-foreground)'};
61
+
62
+ ${props => props.isError && css `
63
+ font-weight: var(--font-bold);
64
+ text-transform: uppercase;
65
+ animation: input-shake 0.3s ease-in-out;
66
+ `}
67
+
68
+ @keyframes input-shake {
69
+ 0%, 100% { transform: translateX(0); }
70
+ 25% { transform: translateX(-4px); }
71
+ 75% { transform: translateX(4px); }
72
+ }
73
+ `;
106
74
  export function Input(_a) {
107
- var { label, error: errorProp, helperText, startAdornment, endAdornment, style, className, format, validate, onBlur, onFocus, value } = _a, props = __rest(_a, ["label", "error", "helperText", "startAdornment", "endAdornment", "style", "className", "format", "validate", "onBlur", "onFocus", "value"]);
75
+ var { label, error: errorProp, helperText, startAdornment, endAdornment, style, className, format, validate, onBlur, onFocus, value, id, required } = _a, props = __rest(_a, ["label", "error", "helperText", "startAdornment", "endAdornment", "style", "className", "format", "validate", "onBlur", "onFocus", "value", "id", "required"]);
108
76
  const [isFocused, setIsFocused] = useState(false);
109
77
  const [internalError, setInternalError] = useState(undefined);
110
78
  const error = errorProp || internalError;
@@ -121,9 +89,8 @@ export function Input(_a) {
121
89
  if (onFocus)
122
90
  onFocus(e);
123
91
  };
124
- // Use formatted value when not focused, otherwise raw value
125
92
  const displayValue = (!isFocused && format && value !== undefined)
126
93
  ? format(value)
127
94
  : value;
128
- return (_jsxs(InputContainer, { style: style, className: className, children: [label && (_jsx(Text, { as: "label", variant: "small", weight: "bold", color: "muted", className: "mb-1 block", children: label })), _jsxs(InputWrapper, { children: [startAdornment && _jsx(Adornment, { position: "start", children: startAdornment }), _jsx(StyledInput, Object.assign({}, props, { value: displayValue, hasStartAdornment: !!startAdornment, hasEndAdornment: !!endAdornment, isError: !!error, onBlur: handleBlur, onFocus: handleFocus })), endAdornment && _jsx(Adornment, { position: "end", children: endAdornment })] }), error && (_jsx(Text, { variant: "caption", color: "error", className: "mt-1", children: error })), !error && helperText && (_jsx(Text, { variant: "caption", color: "muted", className: "mt-1", children: helperText }))] }));
95
+ return (_jsxs(InputContainer, { style: style, className: className, children: [label && (_jsx(Label, { htmlFor: id, required: required, children: label })), _jsxs(InputWrapper, { children: [startAdornment && _jsx(Adornment, { position: "start", children: startAdornment }), _jsx(StyledInput, Object.assign({ id: id, required: required, value: displayValue, hasStartAdornment: !!startAdornment, hasEndAdornment: !!endAdornment, isError: !!error, onBlur: handleBlur, onFocus: handleFocus }, props)), endAdornment && _jsx(Adornment, { position: "end", children: endAdornment })] }), helperText && !error && (_jsx(HelperText, { children: helperText })), error && (_jsx(HelperText, { isError: true, children: error }))] }));
129
96
  }
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
3
+ children: React.ReactNode;
4
+ required?: boolean;
5
+ }
6
+ export declare function Label({ children, required, ...props }: LabelProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import styled from '@emotion/styled';
15
+ import { css } from '@emotion/react';
16
+ const StyledLabel = styled.label `
17
+ font-family: var(--font-heading);
18
+ font-weight: var(--font-bold);
19
+ font-size: var(--text-sm);
20
+ color: var(--foreground);
21
+ text-transform: uppercase;
22
+ letter-spacing: 0.05em;
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 0.25rem;
26
+
27
+ ${props => props.required && css `
28
+ &::after {
29
+ content: '*';
30
+ color: var(--error);
31
+ margin-left: 0.25rem;
32
+ }
33
+ `}
34
+ `;
35
+ export function Label(_a) {
36
+ var { children, required } = _a, props = __rest(_a, ["children", "required"]);
37
+ return _jsx(StyledLabel, Object.assign({ required: required }, props, { children: children }));
38
+ }
@@ -0,0 +1 @@
1
+ export * from './Label';
@@ -0,0 +1 @@
1
+ export * from './Label';
@@ -19,7 +19,7 @@ const StyledLink = styled.a `
19
19
  justify-content: center;
20
20
  font-weight: 700;
21
21
  text-decoration: none;
22
- transition: all 0.15s ease;
22
+ transition: all var(--duration-fast) var(--ease-in-out);
23
23
  cursor: pointer;
24
24
 
25
25
  /* Default Variant */
@@ -9,13 +9,13 @@ const ModalContext = React.createContext({ onClose: () => { } });
9
9
  const Overlay = styled.div `
10
10
  position: fixed;
11
11
  inset: 0;
12
- z-index: 50;
12
+ z-index: var(--z-modal);
13
13
  display: flex;
14
14
  align-items: center;
15
15
  justify-content: center;
16
- padding: 1rem;
16
+ padding: var(--spacing-md);
17
17
  backdrop-filter: blur(4px);
18
- background-color: rgba(0, 0, 0, 0.5);
18
+ background-color: rgba(0, 0, 0, var(--overlay-opacity));
19
19
  animation: fadeIn 0.2s ease-out;
20
20
 
21
21
  @keyframes fadeIn {
@@ -34,21 +34,21 @@ const ContentContainer = styled.div `
34
34
  }
35
35
  `;
36
36
  const StyledHeader = styled(Flex) `
37
- padding: 1.5rem;
37
+ padding: var(--spacing-lg);
38
38
  border-bottom: var(--border-width) solid var(--card-border);
39
39
  background: var(--background);
40
40
 
41
41
  h2 {
42
- font-size: 1.25rem;
42
+ font-size: var(--text-xl);
43
43
  font-weight: bold;
44
44
  margin: 0;
45
45
  }
46
46
  `;
47
47
  const StyledBody = styled.div `
48
- padding: 1.5rem;
48
+ padding: var(--spacing-lg);
49
49
  `;
50
50
  const StyledFooter = styled.div `
51
- padding: 1.5rem;
51
+ padding: var(--spacing-lg);
52
52
  border-top: var(--border-width) solid var(--card-border);
53
53
  background: var(--background);
54
54
  `;
@@ -0,0 +1,8 @@
1
+ interface PaginationProps {
2
+ currentPage: number;
3
+ totalPages: number;
4
+ onPageChange: (page: number) => void;
5
+ className?: string;
6
+ }
7
+ export declare function Pagination({ currentPage, totalPages, onPageChange, className }: PaginationProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,94 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import styled from '@emotion/styled';
4
+ import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react';
5
+ const PaginationNav = styled.nav `
6
+ display: flex;
7
+ justify-content: center;
8
+ align-items: center;
9
+ gap: 0.5rem;
10
+ `;
11
+ const PageButton = styled.button `
12
+ display: inline-flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+ width: 40px;
16
+ height: 40px;
17
+ border: var(--border-width) solid var(--card-border);
18
+ border-radius: var(--radius);
19
+ background-color: ${props => props.isActive ? 'var(--primary)' : 'var(--card-bg)'};
20
+ color: ${props => props.isActive ? 'var(--primary-foreground)' : 'var(--foreground)'};
21
+ font-family: var(--font-heading);
22
+ font-weight: 700;
23
+ cursor: pointer;
24
+ box-shadow: ${props => props.isActive ? 'var(--shadow-sm)' : 'none'};
25
+
26
+ &:hover:not(:disabled) {
27
+ transform: translate(-2px, -2px);
28
+ box-shadow: var(--shadow-sm);
29
+ background-color: var(--primary);
30
+ color: var(--primary-foreground);
31
+ }
32
+
33
+ &:active:not(:disabled) {
34
+ transform: translate(0, 0);
35
+ box-shadow: none;
36
+ }
37
+
38
+ &:disabled {
39
+ opacity: 0.5;
40
+ cursor: not-allowed;
41
+ background-color: var(--muted);
42
+ }
43
+ `;
44
+ export function Pagination({ currentPage, totalPages, onPageChange, className }) {
45
+ const handlePageChange = (page) => {
46
+ if (page >= 1 && page <= totalPages && page !== currentPage) {
47
+ onPageChange(page);
48
+ }
49
+ };
50
+ const renderPageNumbers = () => {
51
+ const pages = [];
52
+ const maxVisible = 7; // Max buttons to show
53
+ if (totalPages <= maxVisible) {
54
+ for (let i = 1; i <= totalPages; i++) {
55
+ pages.push(i);
56
+ }
57
+ }
58
+ else {
59
+ // Complex truncation logic
60
+ if (currentPage <= 4) {
61
+ // Near start: 1 2 3 4 5 ... N
62
+ for (let i = 1; i <= 5; i++)
63
+ pages.push(i);
64
+ pages.push('ellipsis');
65
+ pages.push(totalPages);
66
+ }
67
+ else if (currentPage >= totalPages - 3) {
68
+ // Near end: 1 ... N-4 N-3 N-2 N-1 N
69
+ pages.push(1);
70
+ pages.push('ellipsis');
71
+ for (let i = totalPages - 4; i <= totalPages; i++)
72
+ pages.push(i);
73
+ }
74
+ else {
75
+ // Middle: 1 ... C-1 C C+1 ... N
76
+ pages.push(1);
77
+ pages.push('ellipsis');
78
+ pages.push(currentPage - 1);
79
+ pages.push(currentPage);
80
+ pages.push(currentPage + 1);
81
+ pages.push('ellipsis');
82
+ pages.push(totalPages);
83
+ }
84
+ }
85
+ return pages.map((page, index) => {
86
+ if (page === 'ellipsis') {
87
+ return (_jsx("span", { style: { display: 'flex', alignItems: 'center' }, children: _jsx(MoreHorizontal, { size: 20 }) }, `ellipsis-${index}`));
88
+ }
89
+ const pageNum = page;
90
+ return (_jsx(PageButton, { isActive: currentPage === pageNum, onClick: () => handlePageChange(pageNum), "aria-current": currentPage === pageNum ? 'page' : undefined, children: pageNum }, pageNum));
91
+ });
92
+ };
93
+ return (_jsxs(PaginationNav, { "aria-label": "pagination", className: className, children: [_jsx(PageButton, { onClick: () => handlePageChange(currentPage - 1), disabled: currentPage === 1, "aria-label": "Go to previous page", children: _jsx(ChevronLeft, { size: 20, strokeWidth: 2.5 }) }), renderPageNumbers(), _jsx(PageButton, { onClick: () => handlePageChange(currentPage + 1), disabled: currentPage === totalPages, "aria-label": "Go to next page", children: _jsx(ChevronRight, { size: 20, strokeWidth: 2.5 }) })] }));
94
+ }
@@ -0,0 +1 @@
1
+ export { Pagination } from './Pagination';
@@ -0,0 +1 @@
1
+ export { Pagination } from './Pagination';
@@ -4,7 +4,7 @@ interface PopoverProps {
4
4
  content: React.ReactNode;
5
5
  isOpen: boolean;
6
6
  onClose: () => void;
7
- placement?: 'bottom-start' | 'bottom-end' | 'bottom-center';
7
+ placement?: 'bottom-start' | 'bottom-end' | 'bottom-center' | 'top-start' | 'top-end' | 'top-center';
8
8
  offset?: number;
9
9
  }
10
10
  export declare function Popover({ trigger, content, isOpen, onClose, placement, offset }: PopoverProps): import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,12 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useRef, useEffect, useCallback } from 'react';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useRef, useEffect, useCallback, useLayoutEffect } from 'react';
4
4
  import { createPortal } from 'react-dom';
5
+ import { keyframes } from '@emotion/react';
6
+ const popoverScale = keyframes `
7
+ from { opacity: 0; transform: scale(0.95); }
8
+ to { opacity: 1; transform: scale(1); }
9
+ `;
5
10
  export function Popover({ trigger, content, isOpen, onClose, placement = 'bottom-start', offset = 8 }) {
6
11
  const triggerRef = useRef(null);
7
12
  const contentRef = useRef(null);
@@ -14,42 +19,60 @@ export function Popover({ trigger, content, isOpen, onClose, placement = 'bottom
14
19
  const contentRect = contentRef.current.getBoundingClientRect();
15
20
  const viewportWidth = window.innerWidth;
16
21
  const viewportHeight = window.innerHeight;
17
- let top = triggerRect.bottom + offset;
18
- let left = triggerRect.left;
19
- let origin = 'top left';
20
- // Vertical positioning (flip if not enough space below)
21
- if (top + contentRect.height > viewportHeight && triggerRect.top > contentRect.height) {
22
+ let top = 0;
23
+ let left = 0;
24
+ let origin = 'top center';
25
+ const isTop = placement.startsWith('top');
26
+ if (isTop) {
22
27
  top = triggerRect.top - contentRect.height - offset;
23
28
  origin = 'bottom';
29
+ if (top < 0) {
30
+ top = triggerRect.bottom + offset;
31
+ origin = 'top';
32
+ }
24
33
  }
25
34
  else {
35
+ top = triggerRect.bottom + offset;
26
36
  origin = 'top';
37
+ if (top + contentRect.height > viewportHeight) {
38
+ top = triggerRect.top - contentRect.height - offset;
39
+ origin = 'bottom';
40
+ }
27
41
  }
28
- // Horizontal positioning
29
- if (placement === 'bottom-start') {
42
+ const align = placement.split('-')[1];
43
+ if (align === 'start') {
30
44
  left = triggerRect.left;
45
+ origin += ' left';
31
46
  }
32
- else if (placement === 'bottom-end') {
47
+ else if (align === 'end') {
33
48
  left = triggerRect.right - contentRect.width;
49
+ origin += ' right';
34
50
  }
35
- else if (placement === 'bottom-center') {
51
+ else {
36
52
  left = triggerRect.left + (triggerRect.width / 2) - (contentRect.width / 2);
53
+ origin += ' center';
54
+ }
55
+ // Edge Config
56
+ const padding = 16;
57
+ // Horizontal Clamping
58
+ if (left + contentRect.width > viewportWidth - padding) {
59
+ left = viewportWidth - contentRect.width - padding;
37
60
  }
38
- // Edge detection (keep within viewport)
39
- if (left + contentRect.width > viewportWidth - 16) {
40
- left = viewportWidth - contentRect.width - 16; // 16px padding from right edge
41
- if (origin.includes('left'))
42
- origin = origin.replace('left', 'right');
61
+ if (left < padding) {
62
+ left = padding;
43
63
  }
44
- if (left < 16) {
45
- left = 16; // 16px padding from left edge
46
- if (origin.includes('right'))
47
- origin = origin.replace('right', 'left');
64
+ // Vertical Clamping (Fail-safe)
65
+ if (top < padding) {
66
+ top = padding;
67
+ }
68
+ if (top + contentRect.height > viewportHeight - padding) {
69
+ top = viewportHeight - contentRect.height - padding;
48
70
  }
49
71
  setPosition({ top, left });
50
72
  setTransformOrigin(origin);
51
73
  }, [isOpen, placement, offset]);
52
- useEffect(() => {
74
+ // Use useLayoutEffect for layout measurements to prevent flash
75
+ useLayoutEffect(() => {
53
76
  if (isOpen) {
54
77
  updatePosition();
55
78
  window.addEventListener('resize', updatePosition);
@@ -75,17 +98,12 @@ export function Popover({ trigger, content, isOpen, onClose, placement = 'bottom
75
98
  document.addEventListener('mousedown', handleClickOutside);
76
99
  return () => document.removeEventListener('mousedown', handleClickOutside);
77
100
  }, [isOpen, onClose]);
78
- return (_jsxs(_Fragment, { children: [_jsx("div", { ref: triggerRef, style: { display: 'inline-block' }, children: trigger }), isOpen && typeof document !== 'undefined' && createPortal(_jsxs("div", { ref: contentRef, style: {
101
+ return (_jsxs(_Fragment, { children: [_jsx("div", { ref: triggerRef, style: { display: 'inline-block' }, children: trigger }), isOpen && typeof document !== 'undefined' && createPortal(_jsx("div", { ref: contentRef, style: {
79
102
  position: 'fixed',
80
103
  top: position.top,
81
104
  left: position.left,
82
105
  zIndex: 9999, // High z-index to ensure it's on top
83
106
  transformOrigin: transformOrigin,
84
- animation: 'popoverScale 0.1s ease-out',
85
- }, children: [content, _jsx("style", { jsx: true, global: true, children: `
86
- @keyframes popoverScale {
87
- from { opacity: 0; transform: scale(0.95); }
88
- to { opacity: 1; transform: scale(1); }
89
- }
90
- ` })] }), document.body)] }));
107
+ animation: `${popoverScale} 0.1s ease-out`,
108
+ }, children: content }), document.body)] }));
91
109
  }
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ interface RadioGroupProps {
3
+ name?: string;
4
+ value?: string;
5
+ defaultValue?: string;
6
+ onValueChange?: (value: string) => void;
7
+ disabled?: boolean;
8
+ children: React.ReactNode;
9
+ className?: string;
10
+ }
11
+ export declare function RadioGroup({ name, value: controlledValue, defaultValue, onValueChange, disabled, children, className }: RadioGroupProps): import("react/jsx-runtime").JSX.Element;
12
+ interface RadioGroupItemProps {
13
+ value: string;
14
+ children: React.ReactNode;
15
+ disabled?: boolean;
16
+ className?: string;
17
+ }
18
+ export declare function RadioGroupItem({ value, children, disabled, className }: RadioGroupItemProps): import("react/jsx-runtime").JSX.Element;
19
+ export {};