@moderneinc/neo-styled-components 0.0.0-development → 1.6.1-next.1d7c75

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/index.esm.js CHANGED
@@ -1,11 +1,1094 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { semanticColors, spacing, borderRadius, typography, colors } from '@moderneinc/neo-design';
3
+ import Chip, { chipClasses } from '@mui/material/Chip';
4
+ import { styled } from '@mui/material/styles';
5
+ import ButtonBase, { buttonBaseClasses } from '@mui/material/ButtonBase';
6
+ import CircularProgress from '@mui/material/CircularProgress';
7
+ import IconButton, { iconButtonClasses } from '@mui/material/IconButton';
8
+ import FormControl from '@mui/material/FormControl';
9
+ import FormHelperText, { formHelperTextClasses } from '@mui/material/FormHelperText';
10
+ import InputAdornment from '@mui/material/InputAdornment';
11
+ import InputBase, { inputBaseClasses } from '@mui/material/InputBase';
12
+ import InputLabel, { inputLabelClasses } from '@mui/material/InputLabel';
13
+ import { forwardRef, createElement } from 'react';
14
+ import MuiTab, { tabClasses } from '@mui/material/Tab';
15
+ import MuiTabs, { tabsClasses } from '@mui/material/Tabs';
16
+ import Alert, { alertClasses } from '@mui/material/Alert';
17
+ import Button from '@mui/material/Button';
18
+ import LinearProgress, { linearProgressClasses } from '@mui/material/LinearProgress';
19
+ import Stack from '@mui/material/Stack';
20
+
21
+ const StyledChip$1 = styled(Chip)(({ theme }) => ({
22
+ height: 24,
23
+ padding: `${spacing.spacing_1_4}px ${spacing.spacing_1}px`,
24
+ borderRadius: borderRadius.full,
25
+ fontSize: theme.typography.pxToRem(typography.fontSize.xs),
26
+ lineHeight: 1,
27
+ gap: spacing.spacing_1_2,
28
+ overflow: 'visible',
29
+ [`& .${chipClasses.label}`]: {
30
+ padding: 0,
31
+ overflow: 'visible',
32
+ },
33
+ [`& .${chipClasses.icon}`]: {
34
+ margin: 0,
35
+ width: 12,
36
+ height: 12,
37
+ },
38
+ [`& .${chipClasses.deleteIcon}`]: {
39
+ margin: 0,
40
+ width: 12,
41
+ height: 12,
42
+ },
43
+ // Default (Neutral) state
44
+ [`&.${chipClasses.colorDefault}`]: {
45
+ backgroundColor: semanticColors.status.neutral.light,
46
+ color: semanticColors.status.neutral.dark,
47
+ border: `1px solid ${semanticColors.status.neutral.medium}80`,
48
+ [`& .${chipClasses.icon}, & .${chipClasses.deleteIcon}`]: {
49
+ color: semanticColors.status.neutral.dark,
50
+ },
51
+ },
52
+ // Error state
53
+ [`&.${chipClasses.colorError}`]: {
54
+ backgroundColor: semanticColors.status.error.light,
55
+ color: semanticColors.status.error.dark,
56
+ border: `1px solid ${semanticColors.status.error.medium}80`,
57
+ [`& .${chipClasses.icon}, & .${chipClasses.deleteIcon}`]: {
58
+ color: semanticColors.status.error.dark,
59
+ },
60
+ },
61
+ // Warning state
62
+ [`&.${chipClasses.colorWarning}`]: {
63
+ backgroundColor: semanticColors.status.warning.light,
64
+ color: semanticColors.status.warning.dark,
65
+ border: `1px solid ${semanticColors.status.warning.medium}80`,
66
+ [`& .${chipClasses.icon}, & .${chipClasses.deleteIcon}`]: {
67
+ color: semanticColors.status.warning.dark,
68
+ },
69
+ },
70
+ // Success state
71
+ [`&.${chipClasses.colorSuccess}`]: {
72
+ backgroundColor: semanticColors.status.success.light,
73
+ color: semanticColors.status.success.dark,
74
+ border: `1px solid ${semanticColors.status.success.medium}80`,
75
+ [`& .${chipClasses.icon}, & .${chipClasses.deleteIcon}`]: {
76
+ color: semanticColors.status.success.dark,
77
+ },
78
+ },
79
+ // Info state
80
+ [`&.${chipClasses.colorInfo}`]: {
81
+ backgroundColor: semanticColors.status.info.light,
82
+ color: semanticColors.status.info.dark,
83
+ border: `1px solid ${semanticColors.status.info.medium}80`,
84
+ [`& .${chipClasses.icon}, & .${chipClasses.deleteIcon}`]: {
85
+ color: semanticColors.status.info.dark,
86
+ },
87
+ },
88
+ }));
89
+ /**
90
+ * NeoBadge - Status badge component based on MUI Chip
91
+ *
92
+ * @figma https://www.figma.com/design/fQTkGSFbYyE7LiHuQJsENC/Neo---Moderne-Design-system-w--correct-set-of-tokens?node-id=4091-17230
93
+ *
94
+ * Figma Props Mapping:
95
+ * - state (Neutral|Error|Warning|Success|Info) → color (default|error|warning|success|info)
96
+ * - iconLeading → icon prop (pass React element)
97
+ * - iconTrailing → deleteIcon prop (pass React element)
98
+ * - Label → label prop
99
+ */
100
+ const NeoBadge = (props) => {
101
+ return jsx(StyledChip$1, { ...props });
102
+ };
103
+ NeoBadge.displayName = 'NeoBadge';
104
+
105
+ const StyledButtonBase = styled(ButtonBase, {
106
+ shouldForwardProp: prop => prop !== 'variant' && prop !== 'size' && prop !== 'loading',
107
+ })(({ theme, variant = 'primary', size = 'medium', loading = false }) => {
108
+ // Size configurations using theme.spacing()
109
+ const sizeConfig = {
110
+ small: {
111
+ height: 32,
112
+ padding: `0 ${theme.spacing(2)}`,
113
+ fontSize: theme.typography.pxToRem(14),
114
+ },
115
+ medium: {
116
+ height: 40,
117
+ padding: `0 ${theme.spacing(3)}`,
118
+ fontSize: theme.typography.pxToRem(16),
119
+ },
120
+ };
121
+ const sizeStyles = sizeConfig[size];
122
+ // Base styles shared by all variants
123
+ const baseStyles = {
124
+ height: sizeStyles.height,
125
+ padding: sizeStyles.padding,
126
+ fontSize: sizeStyles.fontSize,
127
+ fontWeight: 500,
128
+ lineHeight: 1.5,
129
+ borderRadius: borderRadius.button,
130
+ textTransform: 'none',
131
+ transition: theme.transitions.create(['background-color', 'border-color', 'color'], {
132
+ duration: theme.transitions.duration.short,
133
+ }),
134
+ position: 'relative',
135
+ minWidth: 'fit-content',
136
+ gap: theme.spacing(1),
137
+ // Disabled state (always show cursor)
138
+ [`&.${buttonBaseClasses.disabled}`]: {
139
+ cursor: 'not-allowed',
140
+ pointerEvents: 'auto',
141
+ },
142
+ // Focus visible for keyboard navigation
143
+ [`&.${buttonBaseClasses.focusVisible}`]: {
144
+ outline: `2px solid ${semanticColors.buttons.primary.focus}`,
145
+ outlineOffset: 2,
146
+ },
147
+ };
148
+ // Variant-specific styles
149
+ const variantStyles = {
150
+ primary: {
151
+ backgroundColor: semanticColors.buttons.primary.default,
152
+ color: theme.palette.common.white,
153
+ border: 'none',
154
+ '&:hover': {
155
+ backgroundColor: semanticColors.buttons.primary.hover,
156
+ },
157
+ '&:active': {
158
+ backgroundColor: semanticColors.buttons.primary.pressed,
159
+ },
160
+ ...(loading
161
+ ? {}
162
+ : {
163
+ [`&.${buttonBaseClasses.disabled}`]: {
164
+ backgroundColor: semanticColors.buttons.primary.disabled,
165
+ color: semanticColors.typography.button.disabled,
166
+ },
167
+ }),
168
+ },
169
+ secondary: {
170
+ backgroundColor: semanticColors.buttons.secondary.defaultBackground,
171
+ color: semanticColors.buttons.primary.default,
172
+ border: `1px solid ${semanticColors.buttons.secondary.defaultBorder}`,
173
+ '&:hover': {
174
+ backgroundColor: semanticColors.buttons.secondary.hoverBackground,
175
+ },
176
+ '&:active': {
177
+ backgroundColor: semanticColors.buttons.secondary.pressedBackground,
178
+ },
179
+ ...(loading
180
+ ? {}
181
+ : {
182
+ [`&.${buttonBaseClasses.disabled}`]: {
183
+ backgroundColor: semanticColors.buttons.secondary.disabledBackground,
184
+ borderColor: semanticColors.buttons.secondary.disabledBorder,
185
+ color: semanticColors.typography.button.disabled,
186
+ },
187
+ }),
188
+ },
189
+ destructive: {
190
+ backgroundColor: semanticColors.buttons.destructive,
191
+ color: theme.palette.common.white,
192
+ border: 'none',
193
+ '&:hover': {
194
+ backgroundColor: semanticColors.status.error.dark,
195
+ },
196
+ '&:active': {
197
+ backgroundColor: semanticColors.status.error.dark,
198
+ filter: 'brightness(0.9)',
199
+ },
200
+ ...(loading
201
+ ? {}
202
+ : {
203
+ [`&.${buttonBaseClasses.disabled}`]: {
204
+ backgroundColor: semanticColors.buttons.primary.disabled,
205
+ color: semanticColors.typography.button.disabled,
206
+ },
207
+ }),
208
+ },
209
+ link: {
210
+ backgroundColor: 'transparent',
211
+ color: semanticColors.buttons.tertiary.default,
212
+ border: 'none',
213
+ padding: `0 ${theme.spacing(1)}`,
214
+ '&:hover': {
215
+ backgroundColor: 'transparent',
216
+ color: semanticColors.buttons.tertiary.hover,
217
+ },
218
+ '&:active': {
219
+ color: semanticColors.buttons.tertiary.pressed,
220
+ },
221
+ ...(loading
222
+ ? {}
223
+ : {
224
+ [`&.${buttonBaseClasses.disabled}`]: {
225
+ backgroundColor: 'transparent',
226
+ color: semanticColors.buttons.tertiary.disabled,
227
+ },
228
+ }),
229
+ },
230
+ linkColor: {
231
+ backgroundColor: 'transparent',
232
+ color: semanticColors.buttons.primary.default,
233
+ border: 'none',
234
+ padding: `0 ${theme.spacing(1)}`,
235
+ '&:hover': {
236
+ backgroundColor: 'transparent',
237
+ color: semanticColors.buttons.primary.hover,
238
+ },
239
+ '&:active': {
240
+ color: semanticColors.buttons.primary.pressed,
241
+ },
242
+ ...(loading
243
+ ? {}
244
+ : {
245
+ [`&.${buttonBaseClasses.disabled}`]: {
246
+ backgroundColor: 'transparent',
247
+ color: semanticColors.buttons.tertiary.disabled,
248
+ },
249
+ }),
250
+ },
251
+ };
252
+ return {
253
+ ...baseStyles,
254
+ ...variantStyles[variant],
255
+ };
256
+ });
257
+ const LoadingSpinner = styled(CircularProgress)(({ theme, $variant }) => {
258
+ // Use appropriate spinner color for each variant to ensure visibility
259
+ const spinnerColors = {
260
+ primary: theme.palette.common.white,
261
+ secondary: semanticColors.buttons.primary.default,
262
+ destructive: theme.palette.common.white,
263
+ link: semanticColors.buttons.primary.default,
264
+ linkColor: semanticColors.buttons.primary.default,
265
+ };
266
+ return {
267
+ color: spinnerColors[$variant],
268
+ };
269
+ });
270
+ /**
271
+ * NeoButton - Text button component based on MUI ButtonBase
272
+ *
273
+ * @figma https://www.figma.com/design/fQTkGSFbYyE7LiHuQJsENC/Neo---Moderne-Design-system---2025?node-id=4086-7590
274
+ *
275
+ * Figma Props Mapping:
276
+ * - Hierarchy (Primary|Secondary|Destructive|Link|Link Color) → variant prop
277
+ * - Size (Small|Medium) → size prop
278
+ * - State=Disabled → disabled prop
279
+ * - State=Loading → loading prop
280
+ * - State=Hover → CSS :hover
281
+ * - State=Pressed → CSS :active
282
+ * - State=Focused → CSS :focus-visible
283
+ */
284
+ const NeoButton = ({ variant = 'primary', size = 'medium', loading = false, children, disabled, ...props }) => {
285
+ return (jsx(StyledButtonBase, { variant: variant, size: size, loading: loading, disabled: disabled || loading, ...props, children: loading ? jsx(LoadingSpinner, { "$variant": variant, size: size === 'small' ? 16 : 20 }) : children }));
286
+ };
287
+ NeoButton.displayName = 'NeoButton';
288
+
289
+ const StyledIconButton = styled(IconButton, {
290
+ shouldForwardProp: prop => prop !== 'size',
291
+ })(({ theme, size = 'medium' }) => {
292
+ // Size configurations using theme.spacing()
293
+ const sizeConfig = {
294
+ small: {
295
+ width: 32,
296
+ height: 32,
297
+ padding: theme.spacing(0.5),
298
+ },
299
+ medium: {
300
+ width: 44,
301
+ height: 44,
302
+ padding: theme.spacing(1.25),
303
+ },
304
+ };
305
+ const sizeStyles = sizeConfig[size];
306
+ // Base styles shared by all variants
307
+ const baseStyles = {
308
+ width: sizeStyles.width,
309
+ height: sizeStyles.height,
310
+ padding: sizeStyles.padding,
311
+ borderRadius: borderRadius.button, // Fully rounded/circular
312
+ transition: theme.transitions.create(['background-color', 'border-color', 'color'], {
313
+ duration: theme.transitions.duration.short,
314
+ }),
315
+ // Disabled state
316
+ [`&.${iconButtonClasses.disabled}`]: {
317
+ cursor: 'not-allowed',
318
+ pointerEvents: 'auto',
319
+ },
320
+ // Focus visible for keyboard navigation
321
+ '&:focus-visible': {
322
+ outline: `2px solid ${semanticColors.buttons.primary.focus}`,
323
+ outlineOffset: 2,
324
+ },
325
+ };
326
+ // Icon button styling - single neutral style
327
+ const iconButtonStyles = {
328
+ backgroundColor: 'transparent',
329
+ color: semanticColors.icons.default,
330
+ border: 'none',
331
+ '&:hover': {
332
+ backgroundColor: semanticColors.icons.hoverBackground,
333
+ color: semanticColors.icons.hover,
334
+ },
335
+ '&:active': {
336
+ color: semanticColors.icons.pressed,
337
+ },
338
+ [`&.${iconButtonClasses.disabled}`]: {
339
+ backgroundColor: 'transparent',
340
+ color: semanticColors.icons.disabled,
341
+ },
342
+ };
343
+ return {
344
+ ...baseStyles,
345
+ ...iconButtonStyles,
346
+ };
347
+ });
348
+ /**
349
+ * NeoIconButton - Icon-only button component based on MUI IconButton
350
+ *
351
+ * Simple, neutral icon button with transparent background and icon color states.
352
+ *
353
+ * @figma https://www.figma.com/design/fQTkGSFbYyE7LiHuQJsENC/Neo---Moderne-Design-system---2025?node-id=4086-7590
354
+ *
355
+ * Figma Props Mapping:
356
+ * - Hierarchy=Icon → Single default style (no variant prop)
357
+ * - Size (Small|Medium) → size prop
358
+ * - State=Disabled → disabled prop
359
+ * - State=Hover → CSS :hover
360
+ * - State=Pressed → CSS :active
361
+ * - State=Focused → CSS :focus-visible
362
+ */
363
+ const NeoIconButton = ({ size = 'medium', ...props }) => {
364
+ return jsx(StyledIconButton, { size: size, ...props });
365
+ };
366
+ NeoIconButton.displayName = 'NeoIconButton';
367
+
368
+ /**
369
+ * @license lucide-react v0.552.0 - ISC
370
+ *
371
+ * This source code is licensed under the ISC license.
372
+ * See the LICENSE file in the root directory of this source tree.
373
+ */
374
+
375
+ const toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
376
+ const toCamelCase = (string) => string.replace(
377
+ /^([A-Z])|[\s-_]+(\w)/g,
378
+ (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()
379
+ );
380
+ const toPascalCase = (string) => {
381
+ const camelCase = toCamelCase(string);
382
+ return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
383
+ };
384
+ const mergeClasses = (...classes) => classes.filter((className, index, array) => {
385
+ return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
386
+ }).join(" ").trim();
387
+ const hasA11yProp = (props) => {
388
+ for (const prop in props) {
389
+ if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
390
+ return true;
391
+ }
392
+ }
393
+ };
394
+
395
+ /**
396
+ * @license lucide-react v0.552.0 - ISC
397
+ *
398
+ * This source code is licensed under the ISC license.
399
+ * See the LICENSE file in the root directory of this source tree.
400
+ */
401
+
402
+ var defaultAttributes = {
403
+ xmlns: "http://www.w3.org/2000/svg",
404
+ width: 24,
405
+ height: 24,
406
+ viewBox: "0 0 24 24",
407
+ fill: "none",
408
+ stroke: "currentColor",
409
+ strokeWidth: 2,
410
+ strokeLinecap: "round",
411
+ strokeLinejoin: "round"
412
+ };
413
+
414
+ /**
415
+ * @license lucide-react v0.552.0 - ISC
416
+ *
417
+ * This source code is licensed under the ISC license.
418
+ * See the LICENSE file in the root directory of this source tree.
419
+ */
420
+
421
+
422
+ const Icon = forwardRef(
423
+ ({
424
+ color = "currentColor",
425
+ size = 24,
426
+ strokeWidth = 2,
427
+ absoluteStrokeWidth,
428
+ className = "",
429
+ children,
430
+ iconNode,
431
+ ...rest
432
+ }, ref) => createElement(
433
+ "svg",
434
+ {
435
+ ref,
436
+ ...defaultAttributes,
437
+ width: size,
438
+ height: size,
439
+ stroke: color,
440
+ strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
441
+ className: mergeClasses("lucide", className),
442
+ ...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
443
+ ...rest
444
+ },
445
+ [
446
+ ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
447
+ ...Array.isArray(children) ? children : [children]
448
+ ]
449
+ )
450
+ );
451
+
452
+ /**
453
+ * @license lucide-react v0.552.0 - ISC
454
+ *
455
+ * This source code is licensed under the ISC license.
456
+ * See the LICENSE file in the root directory of this source tree.
457
+ */
458
+
459
+
460
+ const createLucideIcon = (iconName, iconNode) => {
461
+ const Component = forwardRef(
462
+ ({ className, ...props }, ref) => createElement(Icon, {
463
+ ref,
464
+ iconNode,
465
+ className: mergeClasses(
466
+ `lucide-${toKebabCase(toPascalCase(iconName))}`,
467
+ `lucide-${iconName}`,
468
+ className
469
+ ),
470
+ ...props
471
+ })
472
+ );
473
+ Component.displayName = toPascalCase(iconName);
474
+ return Component;
475
+ };
476
+
477
+ /**
478
+ * @license lucide-react v0.552.0 - ISC
479
+ *
480
+ * This source code is licensed under the ISC license.
481
+ * See the LICENSE file in the root directory of this source tree.
482
+ */
483
+
484
+
485
+ const __iconNode$1 = [
486
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
487
+ ["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
488
+ ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
489
+ ];
490
+ const CircleAlert = createLucideIcon("circle-alert", __iconNode$1);
491
+
492
+ /**
493
+ * @license lucide-react v0.552.0 - ISC
494
+ *
495
+ * This source code is licensed under the ISC license.
496
+ * See the LICENSE file in the root directory of this source tree.
497
+ */
498
+
499
+
500
+ const __iconNode = [
501
+ ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
502
+ ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
503
+ ];
504
+ const X = createLucideIcon("x", __iconNode);
505
+
506
+ const StyledFormControl = styled(FormControl, {
507
+ shouldForwardProp: prop => prop !== 'size',
508
+ })(({ size = 'medium' }) => {
509
+ const sizeConfig = {
510
+ small: {
511
+ gap: spacing.spacing_1,
512
+ },
513
+ medium: {
514
+ gap: spacing.spacing_2,
515
+ },
516
+ };
517
+ return {
518
+ display: 'flex',
519
+ flexDirection: 'column',
520
+ gap: sizeConfig[size].gap,
521
+ width: '100%',
522
+ };
523
+ });
524
+ const StyledInputLabel = styled(InputLabel, {
525
+ shouldForwardProp: prop => prop !== 'size' && prop !== 'infoIcon',
526
+ })(({ theme, size = 'medium' }) => {
527
+ const sizeConfig = {
528
+ small: {
529
+ fontSize: theme.typography.pxToRem(typography.fontSize.xs),
530
+ },
531
+ medium: {
532
+ fontSize: theme.typography.pxToRem(typography.fontSize.sm),
533
+ },
534
+ };
535
+ return {
536
+ position: 'static',
537
+ transform: 'none',
538
+ fontSize: sizeConfig[size].fontSize,
539
+ fontWeight: 500,
540
+ color: semanticColors.typography.input.label,
541
+ display: 'flex',
542
+ alignItems: 'center',
543
+ gap: spacing.spacing_1,
544
+ [`&.${inputLabelClasses.disabled}`]: {
545
+ color: semanticColors.icons.disabled,
546
+ },
547
+ [`&.${inputLabelClasses.error}`]: {
548
+ color: semanticColors.typography.input.label,
549
+ },
550
+ // Required asterisk styling
551
+ [`& .${inputLabelClasses.asterisk}`]: {
552
+ color: semanticColors.status.error.medium,
553
+ },
554
+ };
555
+ });
556
+ const InfoIconWrapper = styled('span')({
557
+ display: 'inline-flex',
558
+ alignItems: 'center',
559
+ color: semanticColors.icons.placeholder,
560
+ });
561
+ const StyledInputBase = styled(InputBase, {
562
+ shouldForwardProp: prop => prop !== 'size' && prop !== 'destructive',
563
+ })(({ theme, size = 'medium', destructive }) => {
564
+ const sizeConfig = {
565
+ small: {
566
+ height: 40,
567
+ padding: `${spacing.spacing_1}px ${spacing.spacing_2}px`,
568
+ fontSize: theme.typography.pxToRem(typography.fontSize.sm),
569
+ borderRadius: borderRadius.input,
570
+ },
571
+ medium: {
572
+ height: 44,
573
+ padding: `${spacing.spacing_2}px ${spacing.spacing_3}px`,
574
+ fontSize: theme.typography.pxToRem(typography.fontSize.default),
575
+ borderRadius: borderRadius.full,
576
+ },
577
+ };
578
+ const sizeStyles = sizeConfig[size];
579
+ return {
580
+ height: sizeStyles.height,
581
+ fontSize: sizeStyles.fontSize,
582
+ borderRadius: sizeStyles.borderRadius,
583
+ backgroundColor: semanticColors.input.background,
584
+ border: `1px solid ${destructive ? semanticColors.status.error.medium : semanticColors.border.input}`,
585
+ transition: theme.transitions.create(['border-color', 'background-color'], {
586
+ duration: theme.transitions.duration.short,
587
+ }),
588
+ [`& .${inputBaseClasses.input}`]: {
589
+ padding: sizeStyles.padding,
590
+ color: semanticColors.typography.input.default,
591
+ '&::placeholder': {
592
+ color: semanticColors.typography.input.placeholder,
593
+ },
594
+ },
595
+ // Hover state
596
+ '&:hover': {
597
+ backgroundColor: semanticColors.input.hoverBackground,
598
+ borderColor: destructive ? semanticColors.status.error.medium : semanticColors.border.input,
599
+ },
600
+ // Focused state
601
+ [`&.${inputBaseClasses.focused}`]: {
602
+ backgroundColor: semanticColors.input.background,
603
+ borderColor: destructive
604
+ ? semanticColors.status.error.medium
605
+ : semanticColors.buttons.primary.default,
606
+ },
607
+ // Disabled state
608
+ [`&.${inputBaseClasses.disabled}`]: {
609
+ backgroundColor: semanticColors.input.disabledBackground,
610
+ borderColor: semanticColors.border.input,
611
+ color: semanticColors.icons.disabled,
612
+ [`& .${inputBaseClasses.input}`]: {
613
+ WebkitTextFillColor: semanticColors.icons.disabled,
614
+ },
615
+ },
616
+ // Error state
617
+ [`&.${inputBaseClasses.error}`]: {
618
+ borderColor: semanticColors.status.error.medium,
619
+ },
620
+ // Adornment styling
621
+ '& .MuiInputAdornment-root': {
622
+ color: destructive ? semanticColors.status.error.medium : semanticColors.icons.placeholder,
623
+ },
624
+ };
625
+ });
626
+ const StyledFormHelperText = styled(FormHelperText, {
627
+ shouldForwardProp: prop => prop !== 'size',
628
+ })(({ theme }) => {
629
+ return {
630
+ fontSize: theme.typography.pxToRem(typography.fontSize.xs),
631
+ color: semanticColors.typography.input.helper,
632
+ [`&.${formHelperTextClasses.error}`]: {
633
+ color: semanticColors.status.error.medium,
634
+ },
635
+ [`&.${formHelperTextClasses.disabled}`]: {
636
+ color: semanticColors.icons.disabled,
637
+ },
638
+ };
639
+ });
640
+ /**
641
+ * NeoInputField - Form input field component using MUI FormControl composition
642
+ *
643
+ * @figma https://www.figma.com/design/fQTkGSFbYyE7LiHuQJsENC/Neo---Moderne-Design-system---2025?node-id=4091-23373
644
+ *
645
+ * Figma Props Mapping:
646
+ * - Size (small|medium) → size prop
647
+ * - Destructive (false|true) → destructive prop
648
+ * - State=Placeholder → empty value with placeholder text
649
+ * - State=Hover → CSS :hover
650
+ * - State=Filled → value prop with text
651
+ * - State=Focused → CSS :focus
652
+ * - State=Disabled → disabled prop
653
+ * - Label → label prop
654
+ * - Required indicator (*) → required prop
655
+ * - Info icon → infoIcon prop
656
+ * - Helper text → helperText prop
657
+ * - Error message → errorMessage prop (shown when destructive=true)
658
+ * - Left icon → startIcon prop
659
+ * - Right icon → endIcon prop (auto error icon when destructive=true)
660
+ */
661
+ const NeoInputField = ({ size = 'medium', destructive = false, label, required = false, infoIcon, helperText, errorMessage, startIcon, endIcon, disabled, id, ...props }) => {
662
+ const inputId = id || `neo-input-${Math.random().toString(36).substring(7)}`;
663
+ const helperTextId = helperText || errorMessage ? `${inputId}-helper-text` : undefined;
664
+ return (jsxs(StyledFormControl, { size: size, error: destructive, disabled: disabled, required: required, children: [label && (jsxs(StyledInputLabel, { htmlFor: inputId, size: size, infoIcon: !!infoIcon, required: required, disabled: disabled, error: destructive, children: [label, infoIcon && jsx(InfoIconWrapper, { children: infoIcon })] })), jsx(StyledInputBase, { id: inputId, size: size, destructive: destructive, disabled: disabled, error: destructive, "aria-describedby": helperTextId, startAdornment: startIcon ? jsx(InputAdornment, { position: "start", children: startIcon }) : undefined, endAdornment: destructive ? (jsx(InputAdornment, { position: "end", children: jsx(CircleAlert, { size: size === 'small' ? 16 : 20 }) })) : endIcon ? (jsx(InputAdornment, { position: "end", children: endIcon })) : undefined, ...props }), (helperText || errorMessage) && (jsx(StyledFormHelperText, { id: helperTextId, error: destructive, disabled: disabled, children: destructive ? errorMessage : helperText }))] }));
665
+ };
666
+ NeoInputField.displayName = 'NeoInputField';
667
+
668
+ const StyledChip = styled(Chip)(({ theme, size, variant, color }) => ({
669
+ padding: 0,
670
+ borderRadius: borderRadius.full,
671
+ fontSize: theme.typography.pxToRem(typography.fontSize.xs),
672
+ fontWeight: typography.fontWeight.medium,
673
+ // Size variants
674
+ ...(size === 'small' && {
675
+ height: 16,
676
+ paddingLeft: spacing.spacing_1_2, // 4px
677
+ paddingRight: spacing.spacing_1_2, // 4px
678
+ }),
679
+ ...(size === 'medium' && {
680
+ height: 18,
681
+ paddingLeft: spacing.spacing_3_4, // 6px
682
+ paddingRight: spacing.spacing_3_4, // 6px
683
+ }),
684
+ ...(size === 'large' && {
685
+ height: 20,
686
+ paddingLeft: spacing.spacing_3_4, // 6px
687
+ paddingRight: spacing.spacing_3_4, // 6px
688
+ }),
689
+ [`& .${chipClasses.label}`]: {
690
+ padding: 0,
691
+ },
692
+ // Outlined variant (light type in Figma) - Neutral/Default
693
+ ...(variant === 'outlined' &&
694
+ color === 'default' && {
695
+ backgroundColor: semanticColors.status.neutral.light,
696
+ color: semanticColors.status.neutral.dark,
697
+ borderColor: colors.grey[200], // base/grey/200
698
+ }),
699
+ // Outlined variant - Error
700
+ ...(variant === 'outlined' &&
701
+ color === 'error' && {
702
+ backgroundColor: semanticColors.status.error.light,
703
+ color: semanticColors.status.error.dark,
704
+ borderColor: colors.red[100], // base/red/100
705
+ }),
706
+ // Outlined variant - Warning
707
+ ...(variant === 'outlined' &&
708
+ color === 'warning' && {
709
+ backgroundColor: semanticColors.status.warning.light,
710
+ color: semanticColors.status.warning.dark,
711
+ borderColor: colors.orange[100], // base/orange/100
712
+ }),
713
+ // Outlined variant - Success
714
+ ...(variant === 'outlined' &&
715
+ color === 'success' && {
716
+ backgroundColor: semanticColors.status.success.light,
717
+ color: semanticColors.status.success.dark,
718
+ borderColor: 'rgba(94, 196, 111, 0.24)', // rgba from Figma
719
+ }),
720
+ // Outlined variant - Info
721
+ ...(variant === 'outlined' &&
722
+ color === 'info' && {
723
+ backgroundColor: semanticColors.status.info.light,
724
+ color: semanticColors.status.info.dark,
725
+ borderColor: colors.digitalBlue[100], // base/digital-blue/100
726
+ }),
727
+ // Outlined variant - Violet
728
+ ...(variant === 'outlined' &&
729
+ color === 'violet' && {
730
+ backgroundColor: `${colors.violet[100]}66`, // rgba(235,213,241,0.4)
731
+ color: colors.violet[600],
732
+ borderColor: colors.violet[100], // base/violet/100
733
+ }),
734
+ // Filled variant (dark type in Figma) - Neutral/Default
735
+ ...(variant === 'filled' &&
736
+ color === 'default' && {
737
+ backgroundColor: semanticColors.status.neutral.medium,
738
+ color: '#ffffff',
739
+ border: 'none',
740
+ }),
741
+ // Filled variant - Error
742
+ ...(variant === 'filled' &&
743
+ color === 'error' && {
744
+ backgroundColor: semanticColors.status.error.medium,
745
+ color: '#ffffff',
746
+ border: 'none',
747
+ }),
748
+ // Filled variant - Warning
749
+ ...(variant === 'filled' &&
750
+ color === 'warning' && {
751
+ backgroundColor: semanticColors.status.warning.medium,
752
+ color: '#ffffff',
753
+ border: 'none',
754
+ }),
755
+ // Filled variant - Success
756
+ ...(variant === 'filled' &&
757
+ color === 'success' && {
758
+ backgroundColor: semanticColors.status.success.medium,
759
+ color: '#ffffff',
760
+ border: 'none',
761
+ }),
762
+ // Filled variant - Info
763
+ ...(variant === 'filled' &&
764
+ color === 'info' && {
765
+ backgroundColor: semanticColors.status.info.medium,
766
+ color: '#ffffff',
767
+ border: 'none',
768
+ }),
769
+ // Filled variant - Violet
770
+ ...(variant === 'filled' &&
771
+ color === 'violet' && {
772
+ backgroundColor: colors.violet[500],
773
+ color: '#ffffff',
774
+ border: 'none',
775
+ }),
776
+ }));
777
+ /**
778
+ * NeoTag - Small pill-shaped label component based on MUI Chip
779
+ *
780
+ * @figma https://www.figma.com/design/fQTkGSFbYyE7LiHuQJsENC/Neo---Moderne-Design-system---2025?node-id=4120-34533
781
+ *
782
+ * Figma Props Mapping:
783
+ * - m (sm|md|lg) → size (small|medium|large)
784
+ * - type (light|dark) → variant (outlined|filled)
785
+ * - state (Neutral|Error|Warning|Success|Info|Violet) → color (default|error|warning|success|info|violet)
786
+ * - Label text → label prop
787
+ */
788
+ const NeoTag = ({ size = 'small', variant = 'outlined', ...props }) => {
789
+ return jsx(StyledChip, { size: size, variant: variant, ...props });
790
+ };
791
+ NeoTag.displayName = 'NeoTag';
792
+
793
+ /**
794
+ * NeoTabs - Tabs container component based on MUI Tabs
795
+ *
796
+ * @figma https://www.figma.com/design/fQTkGSFbYyE7LiHuQJsENC/Neo---Moderne-Design-system---2025?node-id=3368-26152
797
+ *
798
+ * Figma Props Mapping:
799
+ * - Horizontal tab bar → default layout
800
+ * - Tab selection → value prop + onChange
801
+ * - Active indicator → styled via indicator slot
802
+ */
803
+ const NeoTabs = styled(MuiTabs)(() => ({
804
+ minHeight: 40,
805
+ [`& .${tabsClasses.indicator}`]: {
806
+ backgroundColor: semanticColors.border.tabActive,
807
+ height: 2,
808
+ },
809
+ [`& .${tabsClasses.flexContainer}`]: {
810
+ gap: 0,
811
+ },
812
+ }));
813
+ NeoTabs.displayName = 'NeoTabs';
814
+ const StyledTab = styled(MuiTab)(({ theme }) => ({
815
+ minHeight: 40,
816
+ padding: `${theme.spacing(1)} ${theme.spacing(2)}`,
817
+ textTransform: 'none',
818
+ fontWeight: 500,
819
+ fontSize: theme.typography.pxToRem(14),
820
+ lineHeight: 1.5,
821
+ color: semanticColors.typography.tab.inactive,
822
+ transition: theme.transitions.create(['color', 'background-color'], {
823
+ duration: theme.transitions.duration.short,
824
+ }),
825
+ [`&.${tabClasses.selected}`]: {
826
+ color: semanticColors.typography.tab.active,
827
+ fontWeight: 600,
828
+ },
829
+ [`&.${buttonBaseClasses.focusVisible}`]: {
830
+ outline: `2px solid ${semanticColors.buttons.primary.focus}`,
831
+ outlineOffset: -2,
832
+ },
833
+ [`&.${tabClasses.disabled}`]: {
834
+ color: semanticColors.typography.button.disabled,
835
+ cursor: 'not-allowed',
836
+ pointerEvents: 'auto',
837
+ },
838
+ }));
839
+ const TabLabelContainer = styled('span')(({ theme }) => ({
840
+ display: 'inline-flex',
841
+ alignItems: 'center',
842
+ gap: theme.spacing(1),
843
+ }));
844
+ /**
845
+ * NeoTab - Individual tab button component based on MUI Tab
846
+ *
847
+ * @figma https://www.figma.com/design/fQTkGSFbYyE7LiHuQJsENC/Neo---Moderne-Design-system---2025?node-id=3368-26152
848
+ *
849
+ * Figma Props Mapping:
850
+ * - Current=True/False → Controlled by parent NeoTabs value
851
+ * - State=Default → default styling
852
+ * - State=Hover → CSS :hover
853
+ * - State=Focus → CSS :focus-visible
854
+ * - Tag count → count prop (renders NeoTag)
855
+ * - Text label → label prop
856
+ */
857
+ const NeoTab = ({ label, count, ...props }) => {
858
+ const tabLabel = count !== undefined ? (jsxs(TabLabelContainer, { children: [label, jsx(NeoTag, { label: String(count), size: "large", variant: "outlined", color: "default" })] })) : (label);
859
+ return jsx(StyledTab, { label: tabLabel, ...props });
860
+ };
861
+ NeoTab.displayName = 'NeoTab';
862
+
863
+ const StyledAlert = styled(Alert, {
864
+ shouldForwardProp: prop => prop !== 'variant' && prop !== 'showProgress',
865
+ })(({ theme, variant = 'default' }) => {
866
+ // Base styles
867
+ const baseStyles = {
868
+ padding: spacing.spacing_2,
869
+ borderRadius: borderRadius.s,
870
+ gap: spacing.spacing_4,
871
+ alignItems: 'flex-start',
872
+ fontSize: theme.typography.pxToRem(typography.fontSize.sm),
873
+ lineHeight: 1.4,
874
+ [`& .${alertClasses.icon}`]: {
875
+ display: 'none',
876
+ },
877
+ [`& .${alertClasses.message}`]: {
878
+ padding: 0,
879
+ width: '100%',
880
+ display: 'flex',
881
+ flexDirection: 'column',
882
+ gap: spacing.spacing_3,
883
+ },
884
+ [`& .${alertClasses.action}`]: {
885
+ padding: 0,
886
+ marginRight: 0,
887
+ },
888
+ };
889
+ // Variant-specific styles
890
+ const variantStyles = {
891
+ default: {
892
+ backgroundColor: semanticColors.surfaces.snackbarDarkMode,
893
+ color: colors.grey['800'],
894
+ border: `1px solid ${semanticColors.border.primary}`,
895
+ },
896
+ dark: {
897
+ backgroundColor: semanticColors.surfaces.snackbarLightMode,
898
+ color: semanticColors.surfaces.white,
899
+ border: `1px solid ${semanticColors.surfaces.snackbarLightMode}`,
900
+ },
901
+ brand: {
902
+ backgroundColor: semanticColors.surfaces.snackbarBrand,
903
+ color: semanticColors.surfaces.white,
904
+ border: `1px solid ${semanticColors.surfaces.snackbarBrand}`,
905
+ },
906
+ error: {
907
+ backgroundColor: semanticColors.status.error.light,
908
+ color: semanticColors.status.error.dark,
909
+ border: `1px solid ${semanticColors.status.error.medium}`,
910
+ },
911
+ success: {
912
+ backgroundColor: semanticColors.status.success.light,
913
+ color: semanticColors.status.success.dark,
914
+ border: `1px solid ${semanticColors.status.success.medium}`,
915
+ },
916
+ info: {
917
+ backgroundColor: semanticColors.status.info.light,
918
+ color: semanticColors.status.info.dark,
919
+ border: `1px solid ${semanticColors.status.info.medium}`,
920
+ },
921
+ download: {
922
+ backgroundColor: semanticColors.surfaces.snackbarDarkMode,
923
+ color: colors.grey['800'],
924
+ border: `1px solid ${semanticColors.border.primary}`,
925
+ },
926
+ };
927
+ return {
928
+ ...baseStyles,
929
+ ...variantStyles[variant],
930
+ };
931
+ });
932
+ const ToastTitle = styled('p')(({ theme, variant = 'default' }) => {
933
+ const colorMap = {
934
+ default: colors.grey['700'],
935
+ dark: semanticColors.surfaces.white,
936
+ brand: semanticColors.surfaces.white,
937
+ error: semanticColors.status.error.dark,
938
+ success: semanticColors.status.success.dark,
939
+ info: semanticColors.status.info.dark,
940
+ download: colors.grey['700'],
941
+ };
942
+ return {
943
+ margin: 0,
944
+ fontSize: theme.typography.pxToRem(typography.fontSize.sm),
945
+ fontWeight: 600,
946
+ lineHeight: 1.4,
947
+ color: colorMap[variant],
948
+ };
949
+ });
950
+ const ToastMessage = styled('p')(({ theme, variant = 'default' }) => {
951
+ const colorMap = {
952
+ default: semanticColors.typography.bodySecondary,
953
+ dark: colors.grey['200'],
954
+ brand: colors.grey['200'],
955
+ error: semanticColors.status.error.dark,
956
+ success: semanticColors.status.success.dark,
957
+ info: semanticColors.status.info.dark,
958
+ download: colors.grey['800'],
959
+ };
960
+ return {
961
+ margin: 0,
962
+ fontSize: theme.typography.pxToRem(typography.fontSize.sm),
963
+ fontWeight: 400,
964
+ lineHeight: 1.4,
965
+ color: colorMap[variant],
966
+ };
967
+ });
968
+ const ToastActions = styled(Stack)({
969
+ gap: spacing.spacing_3,
970
+ alignItems: 'flex-start',
971
+ justifyContent: 'flex-start',
972
+ });
973
+ const ToastButton = styled(Button, {
974
+ shouldForwardProp: prop => prop !== 'toastVariant' && prop !== 'primary',
975
+ })(({ toastVariant = 'default', primary }) => {
976
+ const getColor = () => {
977
+ if (primary) {
978
+ if (toastVariant === 'dark' || toastVariant === 'brand') {
979
+ return colors.digitalBlue['200'];
980
+ }
981
+ return semanticColors.typography.link.primary;
982
+ }
983
+ // Non-primary button colors
984
+ if (toastVariant === 'dark' || toastVariant === 'brand') {
985
+ return semanticColors.surfaces.white;
986
+ }
987
+ if (toastVariant === 'error') {
988
+ return semanticColors.status.error.dark;
989
+ }
990
+ if (toastVariant === 'success') {
991
+ return semanticColors.status.success.dark;
992
+ }
993
+ if (toastVariant === 'info') {
994
+ return semanticColors.status.info.dark;
995
+ }
996
+ // default and download variants use icon placeholder color
997
+ return semanticColors.icons.placeholder;
998
+ };
999
+ return {
1000
+ padding: 0,
1001
+ minWidth: 'auto',
1002
+ fontSize: typography.fontSize.sm,
1003
+ fontWeight: 500,
1004
+ lineHeight: 'normal',
1005
+ textTransform: 'none',
1006
+ color: getColor(),
1007
+ '&:hover': {
1008
+ backgroundColor: 'transparent',
1009
+ textDecoration: 'underline',
1010
+ },
1011
+ };
1012
+ });
1013
+ const ProgressSection = styled(Stack)({
1014
+ gap: spacing.spacing_1,
1015
+ width: '100%',
1016
+ });
1017
+ const FileName = styled('p')(({ theme }) => ({
1018
+ margin: 0,
1019
+ fontSize: theme.typography.pxToRem(typography.fontSize.xs),
1020
+ fontWeight: 500,
1021
+ lineHeight: 1.5,
1022
+ color: colors.grey['800'],
1023
+ }));
1024
+ const ProgressLabel = styled('p')(({ theme }) => ({
1025
+ margin: 0,
1026
+ fontSize: theme.typography.pxToRem(typography.fontSize.xs),
1027
+ fontWeight: 500,
1028
+ lineHeight: 1.5,
1029
+ color: colors.grey['800'],
1030
+ }));
1031
+ const StyledLinearProgress = styled(LinearProgress)({
1032
+ width: '100%',
1033
+ height: spacing.spacing_2,
1034
+ borderRadius: borderRadius.s,
1035
+ backgroundColor: colors.grey['200'],
1036
+ [`& .${linearProgressClasses.bar}`]: {
1037
+ backgroundColor: semanticColors.buttons.primary.default,
1038
+ borderRadius: `${borderRadius.xS}px 0 0 ${borderRadius.xS}px`,
1039
+ },
1040
+ });
1041
+ const DownloadActions = styled(Stack)({
1042
+ gap: `${spacing.spacing_4}px`,
1043
+ alignItems: 'center',
1044
+ });
1045
+ const DownloadIconButton = styled(IconButton)({
1046
+ padding: 0,
1047
+ width: 16,
1048
+ height: 16,
1049
+ color: colors.grey['800'],
1050
+ '&:hover': {
1051
+ backgroundColor: 'transparent',
1052
+ },
1053
+ });
1054
+ /**
1055
+ * NeoToast - Notification/Toast component based on MUI Alert
1056
+ *
1057
+ * @figma https://www.figma.com/design/fQTkGSFbYyE7LiHuQJsENC/Neo---Moderne-Design-system---2025?node-id=4122-37223
1058
+ *
1059
+ * Figma Props Mapping:
1060
+ * - type (Light mode|Dark mode|Brand color|Error|Success|Info|Download) → variant (default|dark|brand|error|success|info|download)
1061
+ * - header → title (string)
1062
+ * - supportingText → message (string)
1063
+ * - xCloseButton → showClose (boolean)
1064
+ * - actions → actions (ReactNode)
1065
+ * - Progress bar → progress (number 0-100)
1066
+ */
1067
+ const NeoToast = ({ variant = 'default', title, message, showClose = true, actions, progress, fileName, onClose, ...props }) => {
1068
+ const isDownloadVariant = variant === 'download';
1069
+ return (jsx(StyledAlert, { ...props, variant: variant, showProgress: isDownloadVariant, action: showClose && !isDownloadVariant ? (jsx(IconButton, { size: "small", onClick: onClose, sx: { color: semanticColors.icons.placeholder }, children: jsx(X, { size: 16 }) })) : undefined, children: isDownloadVariant ? (jsxs(Fragment, { children: [jsxs(Stack, { direction: "row", sx: {
1070
+ alignItems: 'flex-start',
1071
+ justifyContent: 'space-between',
1072
+ width: '100%',
1073
+ gap: `${spacing.spacing_4}px`,
1074
+ }, children: [title && jsx(ToastTitle, { variant: variant, children: title }), showClose && (jsxs(DownloadActions, { direction: "row", children: [jsx(DownloadIconButton, { size: "small", children: jsx(Stack, { sx: { width: 16, height: 16 } }) }), jsx(DownloadIconButton, { size: "small", onClick: onClose, children: jsx(X, { size: 16 }) })] }))] }), jsxs(ProgressSection, { children: [jsxs(Stack, { direction: "row", sx: { alignItems: 'flex-end', justifyContent: 'space-between', width: '100%' }, children: [fileName && jsx(FileName, { children: fileName }), progress !== undefined && jsxs(ProgressLabel, { children: [progress, "%"] })] }), jsx(StyledLinearProgress, { variant: "determinate", value: progress ?? 0 })] })] })) : (jsxs(Fragment, { children: [jsxs(Stack, { sx: { gap: `${spacing.spacing_1}px`, width: '100%' }, children: [title && jsx(ToastTitle, { variant: variant, children: title }), message && jsx(ToastMessage, { variant: variant, children: message })] }), actions && jsx(ToastActions, { direction: "row", children: actions })] })) }));
1075
+ };
1076
+ /**
1077
+ * Helper component for creating toast action buttons with proper styling
1078
+ */
1079
+ const NeoToastButton = ({ primary, variant = 'default', children, ...props }) => {
1080
+ return (jsx(ToastButton, { variant: "text", toastVariant: variant, primary: primary, ...props, children: children }));
1081
+ };
1082
+ NeoToast.displayName = 'NeoToast';
1083
+ NeoToastButton.displayName = 'NeoToastButton';
1084
+
1
1085
  /**
2
1086
  * @moderneinc/neo-styled-components
3
1087
  *
4
1088
  * Styled MUI components using Moderne design tokens
5
1089
  */
6
1090
 
7
- // Placeholder - components will be added here
8
1091
  const version = '0.0.0-development';
9
1092
 
10
- export { version };
1093
+ export { NeoBadge, NeoButton, NeoIconButton, NeoInputField, NeoTab, NeoTabs, NeoTag, NeoToast, NeoToastButton, version };
11
1094
  //# sourceMappingURL=index.esm.js.map