@neo-design/mui 1.0.0-rc.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.
- package/LICENSE +21 -0
- package/README.md +25 -0
- package/package.json +38 -0
- package/theme/alert-icons.mjs +16 -0
- package/theme/baseline.mjs +17 -0
- package/theme/checkbox-icons.mjs +12 -0
- package/theme/chip-icon.mjs +8 -0
- package/theme/components.mjs +982 -0
- package/theme/custom-css.mjs +44 -0
- package/theme/index.d.ts +9 -0
- package/theme/index.mjs +7 -0
- package/theme/locale.mjs +11 -0
- package/theme/neo.d.ts +105 -0
- package/theme/select-icon.mjs +8 -0
- package/theme/slices.mjs +37 -0
- package/ui/Alert.mjs +39 -0
- package/ui/AspectRatio.mjs +26 -0
- package/ui/Badge.mjs +39 -0
- package/ui/Banner.mjs +57 -0
- package/ui/Button.mjs +31 -0
- package/ui/ButtonIcon.mjs +22 -0
- package/ui/Checkbox.mjs +30 -0
- package/ui/Chips.mjs +16 -0
- package/ui/ChipsGroup.mjs +53 -0
- package/ui/Combobox.mjs +41 -0
- package/ui/EmptyState.mjs +31 -0
- package/ui/Link.mjs +11 -0
- package/ui/Menu.mjs +73 -0
- package/ui/ModalDialog.mjs +14 -0
- package/ui/ModalFullscreen.mjs +20 -0
- package/ui/RadioButton.mjs +23 -0
- package/ui/RadioGroup.mjs +16 -0
- package/ui/Select.mjs +35 -0
- package/ui/Snackbar.mjs +29 -0
- package/ui/Spinner.mjs +12 -0
- package/ui/Tabs.mjs +46 -0
- package/ui/Tag.mjs +16 -0
- package/ui/TextArea.mjs +45 -0
- package/ui/TextField.mjs +47 -0
- package/ui/Toggle.mjs +27 -0
- package/ui/Tooltip.mjs +20 -0
- package/ui/_alias-props.mjs +26 -0
- package/ui/_css-layers.mjs +15 -0
- package/ui/_help.mjs +25 -0
- package/ui/_icon.mjs +43 -0
- package/ui/_inner-input.mjs +7 -0
- package/ui/_keyboard-focus.mjs +24 -0
- package/ui/_modal-base.mjs +95 -0
- package/ui/_overflow.mjs +21 -0
- package/ui/_reveal.mjs +17 -0
- package/ui/_select-value.mjs +18 -0
- package/ui/_warn.mjs +13 -0
- package/ui/index.d.ts +693 -0
- package/ui/index.mjs +35 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/ChipsGroup.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { FormGroup, RadioGroup, Chip, useRadioGroup } from '@mui/material';
|
|
5
|
+
import { NeoChips } from './Chips.mjs';
|
|
6
|
+
import { warn, hasAccessibleName } from './_warn.mjs';
|
|
7
|
+
import { reveal } from './_reveal.mjs';
|
|
8
|
+
const h = React.createElement;
|
|
9
|
+
|
|
10
|
+
const one = (x) => (Array.isArray(x) ? x[0] : x);
|
|
11
|
+
const many = (x) => (x === undefined ? undefined : Array.isArray(x) ? x : [x]);
|
|
12
|
+
|
|
13
|
+
function RadioChip({ value, label, disabled }) {
|
|
14
|
+
const group = useRadioGroup();
|
|
15
|
+
const checked = group != null && String(group.value) === String(value);
|
|
16
|
+
return h(Chip, {
|
|
17
|
+
component: 'label', variant: checked ? 'filled' : 'outlined', disabled,
|
|
18
|
+
'data-selected': checked ? 'true' : undefined,
|
|
19
|
+
label: h(React.Fragment, null,
|
|
20
|
+
h('input', { type: 'radio', name: group?.name, value, checked, disabled, onChange: group?.onChange }),
|
|
21
|
+
label),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const NeoChipsGroup = React.forwardRef(function NeoChipsGroup({ selection = 'single', options = [], value, defaultValue, disabled = false, onChange, ...rest }, ref) {
|
|
26
|
+
hasAccessibleName('NeoChipsGroup', undefined, rest);
|
|
27
|
+
const unlabeled = options.filter((o) => o && (o.label === undefined || String(o.label).trim() === ''));
|
|
28
|
+
if (unlabeled.length) warn(`NeoChipsGroup: ${unlabeled.length} option(s) without \`label\` — the chip has no accessible name.`);
|
|
29
|
+
const [own, setOwn] = React.useState(many(defaultValue) || []);
|
|
30
|
+
const className = ['chips-group', rest.className].filter(Boolean).join(' ');
|
|
31
|
+
|
|
32
|
+
if (selection === 'single') {
|
|
33
|
+
return h(RadioGroup, {
|
|
34
|
+
row: true, value: one(value), defaultValue: one(defaultValue),
|
|
35
|
+
onChange: (e, v) => { reveal(e.target, '.MuiChip-root', { inline: 'center' }); onChange?.(e, v); },
|
|
36
|
+
...rest, className, ref,
|
|
37
|
+
}, options.map((o) => h(RadioChip, { key: o.value, value: o.value, label: o.label, disabled: o.disabled || disabled })));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const chosen = many(value) || own;
|
|
41
|
+
const toggle = (e, v) => {
|
|
42
|
+
const next = chosen.includes(v) ? chosen.filter((x) => x !== v) : [...chosen, v];
|
|
43
|
+
if (value === undefined) setOwn(next);
|
|
44
|
+
if (next.includes(v)) reveal(e.currentTarget, '.MuiChip-root', { inline: 'center' });
|
|
45
|
+
onChange?.(e, next);
|
|
46
|
+
};
|
|
47
|
+
return h(FormGroup, { row: true, role: 'group', ...rest, className, ref },
|
|
48
|
+
options.map((o) => h(NeoChips, {
|
|
49
|
+
key: o.value, label: o.label, selected: chosen.includes(o.value),
|
|
50
|
+
disabled: o.disabled || disabled, onClick: (e) => toggle(e, o.value),
|
|
51
|
+
onDelete: chosen.includes(o.value) ? (e) => toggle(e, o.value) : undefined,
|
|
52
|
+
})));
|
|
53
|
+
});
|
package/ui/Combobox.mjs
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/Combobox.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { FormControl, FormLabel, Autocomplete, TextField, FormHelperText, InputAdornment } from '@mui/material';
|
|
5
|
+
import { renderIcon } from './_icon.mjs';
|
|
6
|
+
import { helpIcon } from './_help.mjs';
|
|
7
|
+
import { useKeyboardFocus } from './_keyboard-focus.mjs';
|
|
8
|
+
import { hasAccessibleName } from './_warn.mjs';
|
|
9
|
+
const h = React.createElement;
|
|
10
|
+
|
|
11
|
+
export const NeoCombobox = React.forwardRef(function NeoCombobox({ label, required = false, helperText, error = false, readOnly = false, disabled = false, tooltip, iconLeft, iconRight, tooltipLabel, id, placeholder, options = [], onChange, ...rest }, ref) {
|
|
12
|
+
const autoId = React.useId();
|
|
13
|
+
const fieldId = id || autoId;
|
|
14
|
+
const helpId = helperText ? `${fieldId}-helper` : undefined;
|
|
15
|
+
const labelId = label ? `${fieldId}-label` : undefined;
|
|
16
|
+
hasAccessibleName('NeoCombobox', label, rest);
|
|
17
|
+
const keyboardFocus = useKeyboardFocus();
|
|
18
|
+
const helpNode = helpIcon({ text: tooltip, name: tooltipLabel, label, comp: 'Combobox', prefix: 'combobox' });
|
|
19
|
+
const labelNode = label ? h(FormLabel, { id: labelId, htmlFor: fieldId, required }, label) : null;
|
|
20
|
+
return h(FormControl, { error, disabled, fullWidth: true, variant: 'outlined', onFocus: keyboardFocus.onFocus, onBlur: keyboardFocus.onBlur, onPointerDown: keyboardFocus.onPointerDown, ref },
|
|
21
|
+
label && helpNode ? h('div', { className: 'combobox__label-row' }, labelNode, helpNode) : labelNode,
|
|
22
|
+
h(Autocomplete, {
|
|
23
|
+
id: fieldId, fullWidth: true, blurOnSelect: true, options, disabled, readOnly,
|
|
24
|
+
clearIcon: renderIcon('system-error'),
|
|
25
|
+
onChange,
|
|
26
|
+
renderInput: (params) => h(TextField, {
|
|
27
|
+
...params, placeholder, error, disabled,
|
|
28
|
+
inputProps: { ...params.inputProps, 'aria-describedby': helpId },
|
|
29
|
+
InputProps: {
|
|
30
|
+
...params.InputProps,
|
|
31
|
+
className: [params.InputProps.className, keyboardFocus.active ? 'field--focus' : null].filter(Boolean).join(' '),
|
|
32
|
+
readOnly,
|
|
33
|
+
startAdornment: iconLeft ? h(React.Fragment, null, h(InputAdornment, { position: 'start' }, renderIcon(iconLeft, 'combobox__icon')), params.InputProps.startAdornment) : params.InputProps.startAdornment,
|
|
34
|
+
endAdornment: iconRight ? h(React.Fragment, null, h(InputAdornment, { position: 'end' }, renderIcon(iconRight, 'combobox__icon')), params.InputProps.endAdornment) : params.InputProps.endAdornment,
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
...rest,
|
|
38
|
+
}),
|
|
39
|
+
helperText ? h(FormHelperText, { id: helpId, key: error ? 'error' : 'helper', role: error ? 'alert' : undefined }, helperText) : null,
|
|
40
|
+
);
|
|
41
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/EmptyState.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
const h = React.createElement;
|
|
5
|
+
|
|
6
|
+
const LEVELS = ['h2', 'h3', 'h4'];
|
|
7
|
+
|
|
8
|
+
export const NeoEmptyState = React.forwardRef(function NeoEmptyState({
|
|
9
|
+
icon, title, description, actions, headingLevel = 'h2', className, ...rest
|
|
10
|
+
}, ref) {
|
|
11
|
+
const warn = (m) => {
|
|
12
|
+
if (process.env.NODE_ENV !== 'production') console.warn(`[Neo] EmptyState: ${m}`);
|
|
13
|
+
};
|
|
14
|
+
if (!title) warn('without a title the block does not say what is missing.');
|
|
15
|
+
if (!description) warn('the description is required: a title only says WHAT is missing, not what to do.');
|
|
16
|
+
if (!LEVELS.includes(headingLevel)) warn(`headingLevel '${headingLevel}' is not in the system: ${LEVELS.join(', ')}`);
|
|
17
|
+
|
|
18
|
+
return h('div', { className: 'empty-state-shell', ref }, h('div', {
|
|
19
|
+
className: ['empty-state', className].filter(Boolean).join(' '),
|
|
20
|
+
role: 'status',
|
|
21
|
+
'aria-live': 'polite',
|
|
22
|
+
...rest,
|
|
23
|
+
},
|
|
24
|
+
h('div', { className: 'empty-state__body' },
|
|
25
|
+
icon ? h('div', { className: 'empty-state__icon-box' },
|
|
26
|
+
h('div', { className: 'empty-state__icon', 'aria-hidden': 'true' }, icon)) : null,
|
|
27
|
+
h('div', { className: 'empty-state__content' },
|
|
28
|
+
title ? h(LEVELS.includes(headingLevel) ? headingLevel : 'h2', { className: 'empty-state__title' }, title) : null,
|
|
29
|
+
description ? h('p', { className: 'empty-state__description' }, description) : null)),
|
|
30
|
+
actions ? h('div', { className: 'empty-state__actions' }, actions) : null));
|
|
31
|
+
});
|
package/ui/Link.mjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/Link.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Link } from '@mui/material';
|
|
5
|
+
const h = React.createElement;
|
|
6
|
+
|
|
7
|
+
export const NeoLink = React.forwardRef(function NeoLink({ label, disabled, ...rest }, ref) {
|
|
8
|
+
const mui = {};
|
|
9
|
+
if (disabled) { delete rest.href; mui.role = 'link'; mui["aria-disabled"] = 'true'; }
|
|
10
|
+
return h(Link, { ...mui, ...rest, ref }, ...(label !== undefined ? [label] : []));
|
|
11
|
+
});
|
package/ui/Menu.mjs
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/Menu.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { withAliases } from './_alias-props.mjs';
|
|
5
|
+
import { Menu, MenuItem, Divider, ListItemIcon } from '@mui/material';
|
|
6
|
+
import { renderIcon } from './_icon.mjs';
|
|
7
|
+
const h = React.createElement;
|
|
8
|
+
|
|
9
|
+
function mergeRefs(a, b) {
|
|
10
|
+
return React.useMemo(() => {
|
|
11
|
+
if (a == null && b == null) return null;
|
|
12
|
+
return (node) => {
|
|
13
|
+
for (const r of [a, b]) {
|
|
14
|
+
if (typeof r === 'function') r(node);
|
|
15
|
+
else if (r) r.current = node;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}, [a, b]);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const NeoMenu = React.forwardRef(function NeoMenu(rawProps, ref) {
|
|
22
|
+
const { trigger, items = [], 'aria-label': ariaLabel, defaultOpen = false, ...rest } = withAliases('NeoMenu', rawProps);
|
|
23
|
+
const role = 'menu';
|
|
24
|
+
const auto = React.useId();
|
|
25
|
+
const [anchor, setAnchor] = React.useState(null);
|
|
26
|
+
const isOpen = Boolean(anchor);
|
|
27
|
+
const panelId = `neo-menu-${auto}`;
|
|
28
|
+
const idTrigger = `neo-menu-${auto}-trigger`;
|
|
29
|
+
|
|
30
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
31
|
+
if (!trigger) console.warn('[Neo] Menu: `trigger` is missing — without a trigger the panel does not open.');
|
|
32
|
+
if (!ariaLabel) console.warn('[Neo] Menu: `aria-label` is missing — the screen reader does not say what the menu is for.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const triggerRef = mergeRefs(trigger?.ref ?? null, ref);
|
|
36
|
+
const triggerNode = React.useRef(null);
|
|
37
|
+
const storeNode = React.useCallback((n) => {
|
|
38
|
+
triggerNode.current = n;
|
|
39
|
+
if (typeof triggerRef === 'function') triggerRef(n);
|
|
40
|
+
else if (triggerRef) triggerRef.current = n;
|
|
41
|
+
}, [triggerRef]);
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
if (defaultOpen && triggerNode.current) setAnchor(triggerNode.current);
|
|
44
|
+
}, [defaultOpen]);
|
|
45
|
+
|
|
46
|
+
const triggerElement = trigger && React.cloneElement(trigger, {
|
|
47
|
+
ref: storeNode,
|
|
48
|
+
id: idTrigger,
|
|
49
|
+
'aria-haspopup': role,
|
|
50
|
+
'aria-expanded': isOpen ? 'true' : undefined,
|
|
51
|
+
'aria-controls': isOpen ? panelId : undefined,
|
|
52
|
+
onClick: (e) => { setAnchor(e.currentTarget); trigger.props.onClick?.(e); },
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return h(React.Fragment, null,
|
|
56
|
+
triggerElement,
|
|
57
|
+
h(Menu, {
|
|
58
|
+
id: panelId, anchorEl: anchor, open: isOpen, onClose: () => setAnchor(null),
|
|
59
|
+
MenuListProps: { role, 'aria-labelledby': idTrigger, 'aria-label': ariaLabel, disabledItemsFocusable: true },
|
|
60
|
+
...rest,
|
|
61
|
+
}, ...items.flatMap((it, i) => (
|
|
62
|
+
it.divider
|
|
63
|
+
? [h(Divider, { key: `d${i}` })]
|
|
64
|
+
: [h(MenuItem, {
|
|
65
|
+
key: it.value ?? i,
|
|
66
|
+
disabled: it.disabled,
|
|
67
|
+
onClick: () => { setAnchor(null); it.onSelect?.(it.value); },
|
|
68
|
+
},
|
|
69
|
+
it.icon ? h(ListItemIcon, { key: 'i', 'aria-hidden': 'true' }, renderIcon(it.icon, 'menu-item__icon')) : null,
|
|
70
|
+
h('span', { key: 'l', className: 'NeoMenu-label' }, it.label))]
|
|
71
|
+
))),
|
|
72
|
+
);
|
|
73
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/ModalDialog.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { withAliases } from './_alias-props.mjs';
|
|
5
|
+
import { ModalDialogBase } from './_modal-base.mjs';
|
|
6
|
+
const h = React.createElement;
|
|
7
|
+
|
|
8
|
+
export const NeoModalDialog = React.forwardRef(function NeoModalDialog(rawProps, ref) {
|
|
9
|
+
const { open, onClose, title, description, size = 'xs', type = 'default', closable = true, actions, children, ...rest } = withAliases('NeoModalDialog', rawProps);
|
|
10
|
+
return h(ModalDialogBase, {
|
|
11
|
+
open, onClose, title, description, size, type, closable, actions,
|
|
12
|
+
...rest, ref,
|
|
13
|
+
}, children);
|
|
14
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/ModalFullscreen.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { withAliases } from './_alias-props.mjs';
|
|
5
|
+
import { NeoButtonIcon } from './ButtonIcon.mjs';
|
|
6
|
+
import { renderIcon } from './_icon.mjs';
|
|
7
|
+
import { ModalDialogBase } from './_modal-base.mjs';
|
|
8
|
+
const h = React.createElement;
|
|
9
|
+
|
|
10
|
+
export const NeoModalFullscreen = React.forwardRef(function NeoModalFullscreen(rawProps, ref) {
|
|
11
|
+
const { open, onClose, onBack, title, actions, children, ...rest } = withAliases('NeoModalFullscreen', rawProps);
|
|
12
|
+
return h(ModalDialogBase, {
|
|
13
|
+
open, onClose, title, actions, fullScreen: true, closable: true,
|
|
14
|
+
back: onBack
|
|
15
|
+
? h(NeoButtonIcon, { onClick: onBack, 'aria-label': 'Volver', variant: 'tertiary', className: 'NeoModalFullscreen-back' },
|
|
16
|
+
renderIcon('system-nav-left'))
|
|
17
|
+
: null,
|
|
18
|
+
...rest, ref,
|
|
19
|
+
}, children);
|
|
20
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/RadioButton.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { FormControlLabel, Radio } from '@mui/material';
|
|
5
|
+
import { hasAccessibleName } from './_warn.mjs';
|
|
6
|
+
const h = React.createElement;
|
|
7
|
+
|
|
8
|
+
export const NeoRadioButton = React.forwardRef(function NeoRadioButton({ label, checked, defaultChecked, disabled, onChange, id, hideLabel = false, ...rest }, ref) {
|
|
9
|
+
const autoId = React.useId();
|
|
10
|
+
const fieldId = id || autoId;
|
|
11
|
+
hasAccessibleName('NeoRadioButton', label, rest);
|
|
12
|
+
return h(FormControlLabel, {
|
|
13
|
+
ref,
|
|
14
|
+
htmlFor: fieldId, label, className: hideLabel ? 'neo-label-hidden' : undefined,
|
|
15
|
+
control: h(Radio, {
|
|
16
|
+
id: fieldId, disabled,
|
|
17
|
+
...(checked !== undefined ? { checked } : {}),
|
|
18
|
+
...(defaultChecked !== undefined ? { defaultChecked } : {}),
|
|
19
|
+
onChange,
|
|
20
|
+
...rest,
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/RadioGroup.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { FormControl, FormLabel, RadioGroup } from '@mui/material';
|
|
5
|
+
import { warn, hasAccessibleName } from './_warn.mjs';
|
|
6
|
+
const h = React.createElement;
|
|
7
|
+
|
|
8
|
+
export const NeoRadioGroup = React.forwardRef(function NeoRadioGroup({ name, legend, value, defaultValue, direction = 'vertical', disabled = false, onChange, children, ...rest }, ref) {
|
|
9
|
+
hasAccessibleName('NeoRadioGroup', legend, rest);
|
|
10
|
+
if (!name) warn("NeoRadioGroup: without a `name`, two radios do NOT exclude each other even when they look like one group.");
|
|
11
|
+
const legendId = `neo-radio-${React.useId()}-legend`;
|
|
12
|
+
return h(FormControl, { component: 'fieldset', disabled, ref },
|
|
13
|
+
h(FormLabel, { component: 'legend', id: legendId }, legend),
|
|
14
|
+
h(RadioGroup, { 'aria-labelledby': legendId, name, value, defaultValue, row: direction === 'horizontal', onChange, ...rest }, children),
|
|
15
|
+
);
|
|
16
|
+
});
|
package/ui/Select.mjs
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/Select.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { FormControl, FormLabel, Select, OutlinedInput, FormHelperText } from '@mui/material';
|
|
5
|
+
import { helpIcon } from './_help.mjs';
|
|
6
|
+
import { selectValueProps } from './_select-value.mjs';
|
|
7
|
+
import { hasAccessibleName } from './_warn.mjs';
|
|
8
|
+
const h = React.createElement;
|
|
9
|
+
|
|
10
|
+
export const NeoSelect = React.forwardRef(function NeoSelect({ label, required = false, helperText, error = false, readOnly = false, disabled = false, tooltip, tooltipLabel, id, placeholder, renderValue, children, ...rest }, ref) {
|
|
11
|
+
const autoId = React.useId();
|
|
12
|
+
const fieldId = id || autoId;
|
|
13
|
+
const helpId = helperText ? `${fieldId}-helper` : undefined;
|
|
14
|
+
const [openOwn, setOpenOwn] = React.useState(Boolean(rest.defaultOpen));
|
|
15
|
+
const open = rest.open ?? openOwn;
|
|
16
|
+
const labelId = label ? `${fieldId}-label` : undefined;
|
|
17
|
+
hasAccessibleName('NeoSelect', label, rest);
|
|
18
|
+
const helpNode = helpIcon({ text: tooltip, name: tooltipLabel, label, comp: 'Select', prefix: 'select' });
|
|
19
|
+
const labelNode = label ? h(FormLabel, { id: labelId, htmlFor: fieldId, required }, label) : null;
|
|
20
|
+
return h(FormControl, { error, disabled, fullWidth: true, variant: 'outlined', ref },
|
|
21
|
+
label && helpNode ? h('div', { className: 'select__label-row' }, labelNode, helpNode) : labelNode,
|
|
22
|
+
h(Select, {
|
|
23
|
+
id: fieldId, readOnly,
|
|
24
|
+
labelId,
|
|
25
|
+
SelectDisplayProps: { 'aria-invalid': error ? 'true' : undefined, 'aria-readonly': readOnly ? 'true' : undefined, ...(open ? null : { 'aria-controls': undefined }) },
|
|
26
|
+
...selectValueProps({ placeholder, renderValue, children, value: rest.value, defaultValue: rest.defaultValue }),
|
|
27
|
+
input: h(OutlinedInput, { notched: false }),
|
|
28
|
+
'aria-describedby': helpId,
|
|
29
|
+
...rest,
|
|
30
|
+
MenuProps: { ...rest.MenuProps, MenuListProps: { disabledItemsFocusable: true, ...(rest.MenuProps || {}).MenuListProps } },
|
|
31
|
+
onOpen: (e) => { setOpenOwn(true); rest.onOpen?.(e); }, onClose: (e) => { setOpenOwn(false); rest.onClose?.(e); },
|
|
32
|
+
}, children),
|
|
33
|
+
helperText ? h(FormHelperText, { id: helpId, key: error ? 'error' : 'helper', role: error ? 'alert' : undefined }, helperText) : null,
|
|
34
|
+
);
|
|
35
|
+
});
|
package/ui/Snackbar.mjs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/Snackbar.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Snackbar, SnackbarContent } from '@mui/material';
|
|
5
|
+
import { NeoButtonIcon } from './ButtonIcon.mjs';
|
|
6
|
+
import { renderIcon } from './_icon.mjs';
|
|
7
|
+
const h = React.createElement;
|
|
8
|
+
|
|
9
|
+
const ACCION = { variant: 'secondary', size: 'small', surface: 'inverse' };
|
|
10
|
+
const conVariante = (node) => (React.isValidElement(node) && typeof node.type !== 'string'
|
|
11
|
+
? React.cloneElement(node, Object.fromEntries(Object.entries(ACCION).filter(([k]) => node.props[k] === undefined)))
|
|
12
|
+
: node);
|
|
13
|
+
|
|
14
|
+
export const NeoSnackbar = React.forwardRef(function NeoSnackbar({ open, onClose, message, actions, closable = true, ...rest }, ref) {
|
|
15
|
+
if (!message && process.env.NODE_ENV !== 'production') {
|
|
16
|
+
console.warn('[Neo] Snackbar: the message is missing — a snackbar without text communicates nothing.');
|
|
17
|
+
}
|
|
18
|
+
const acciones = actions ? h('span', { className: 'NeoSnackbar-action', key: 'a' }, React.Children.map(actions, conVariante)) : null;
|
|
19
|
+
const equis = closable ? h(NeoButtonIcon, { variant: 'tertiary', size: 'small', surface: 'inverse', className: 'NeoSnackbar-close', key: 'c', onClick: (e) => onClose && onClose(e, 'closeButton'), 'aria-label': 'Cerrar aviso' }, renderIcon('system-close')) : null;
|
|
20
|
+
return h(Snackbar, { ref, open, onClose, ...rest },
|
|
21
|
+
h(SnackbarContent, {
|
|
22
|
+
role: 'status',
|
|
23
|
+
'aria-live': 'polite',
|
|
24
|
+
message: h('span', { className: 'NeoSnackbar-message' },
|
|
25
|
+
h('span', { className: 'NeoSnackbar-icon', key: 'i' }, renderIcon('semantic-info-inverse')),
|
|
26
|
+
h('span', { className: 'NeoSnackbar-text', key: 't' }, message)),
|
|
27
|
+
action: acciones || equis ? [acciones, equis] : undefined,
|
|
28
|
+
}));
|
|
29
|
+
});
|
package/ui/Spinner.mjs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/Spinner.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { CircularProgress } from '@mui/material';
|
|
5
|
+
const h = React.createElement;
|
|
6
|
+
|
|
7
|
+
export const NeoSpinner = React.forwardRef(function NeoSpinner({ size = 'small', ...rest }, ref) {
|
|
8
|
+
const mui = {};
|
|
9
|
+
if (size === 'small') { mui.size = 'sm'; }
|
|
10
|
+
if (size === 'large') { mui.size = 'lg'; }
|
|
11
|
+
return h(CircularProgress, { ...mui, ...rest, ref });
|
|
12
|
+
});
|
package/ui/Tabs.mjs
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/Tabs.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Tabs, Tab } from '@mui/material';
|
|
5
|
+
import { renderIcon } from './_icon.mjs';
|
|
6
|
+
import { reveal } from './_reveal.mjs';
|
|
7
|
+
import { watchOverflow } from './_overflow.mjs';
|
|
8
|
+
const h = React.createElement;
|
|
9
|
+
|
|
10
|
+
export const NeoTabs = React.forwardRef(function NeoTabs({ items = [], value, defaultValue, onChange, 'aria-label': ariaLabel, ...rest }, ref) {
|
|
11
|
+
const auto = React.useId();
|
|
12
|
+
const [internal, setInternal] = React.useState(defaultValue ?? items[0]?.value);
|
|
13
|
+
const activeValue = value !== undefined ? value : internal;
|
|
14
|
+
const rowRef = React.useRef(null);
|
|
15
|
+
React.useEffect(() => watchOverflow(rowRef.current, '[role="tab"]'), [items.length]);
|
|
16
|
+
const setRef = React.useCallback((n) => { rowRef.current = n; if (typeof ref === 'function') ref(n); else if (ref) ref.current = n; }, [ref]);
|
|
17
|
+
|
|
18
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
19
|
+
if (!ariaLabel) console.warn('[Neo] Tabs: `aria-label` is missing — without it the screen reader does not say what the list is for.');
|
|
20
|
+
if (items.length < 2) console.warn('[Neo] Tabs: with fewer than two sections there is nothing to navigate between.');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const id = (v, part) => `neo-tabs-${auto}-${part}-${v}`;
|
|
24
|
+
const handleChange = (e, v) => { reveal(e.target, '[role="tab"]', { inline: 'center', settle: true }); if (value === undefined) setInternal(v); onChange?.(e, v); };
|
|
25
|
+
|
|
26
|
+
return h(React.Fragment, null,
|
|
27
|
+
h(Tabs, { value: activeValue, onChange: handleChange, 'aria-label': ariaLabel, ...rest, ref: setRef },
|
|
28
|
+
...items.map((it) => h(Tab, {
|
|
29
|
+
key: it.value,
|
|
30
|
+
value: it.value,
|
|
31
|
+
label: it.label,
|
|
32
|
+
icon: renderIcon(it.icon),
|
|
33
|
+
disabled: it.disabled,
|
|
34
|
+
id: id(it.value, 'tab'),
|
|
35
|
+
'aria-controls': id(it.value, 'panel'),
|
|
36
|
+
}))),
|
|
37
|
+
...items.map((it) => h('div', {
|
|
38
|
+
key: it.value,
|
|
39
|
+
role: 'tabpanel',
|
|
40
|
+
id: id(it.value, 'panel'),
|
|
41
|
+
'aria-labelledby': id(it.value, 'tab'),
|
|
42
|
+
tabIndex: 0,
|
|
43
|
+
hidden: it.value !== activeValue,
|
|
44
|
+
}, it.value === activeValue ? it.content : null)),
|
|
45
|
+
);
|
|
46
|
+
});
|
package/ui/Tag.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/Tag.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Chip } from '@mui/material';
|
|
5
|
+
import { renderIcon } from './_icon.mjs';
|
|
6
|
+
const h = React.createElement;
|
|
7
|
+
|
|
8
|
+
export const NeoTag = React.forwardRef(function NeoTag({ tone = 'soft', label, icon, iconRight, ...rest }, ref) {
|
|
9
|
+
const mui = {};
|
|
10
|
+
mui.variant = 'tag';
|
|
11
|
+
mui.clickable = false;
|
|
12
|
+
if (tone === 'solid') { mui["data-tone"] = 'solid'; }
|
|
13
|
+
mui.label = iconRight ? [h(React.Fragment, { key: 'l' }, label), h(React.Fragment, { key: 'i' }, renderIcon(iconRight))] : label;
|
|
14
|
+
if (icon) mui.icon = renderIcon(icon);
|
|
15
|
+
return h(Chip, { ...mui, ...rest, ref });
|
|
16
|
+
});
|
package/ui/TextArea.mjs
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/TextArea.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { FormControl, FormLabel, OutlinedInput, FormHelperText } from '@mui/material';
|
|
5
|
+
import { helpIcon } from './_help.mjs';
|
|
6
|
+
import { useKeyboardFocus } from './_keyboard-focus.mjs';
|
|
7
|
+
import { warn, hasAccessibleName } from './_warn.mjs';
|
|
8
|
+
const h = React.createElement;
|
|
9
|
+
|
|
10
|
+
export const NeoTextArea = React.forwardRef(function NeoTextArea({ label, required = false, helperText, error = false, readOnly = false, disabled = false, tooltip, counter, tooltipLabel, id, inputMode, maxLength, inputProps: consumerInputProps, value, defaultValue, onChange, minRows = 3, maxRows, rows, ...rest }, ref) {
|
|
11
|
+
const inputAttrs = { ...(inputMode != null ? { inputMode } : {}), ...(maxLength != null ? { maxLength } : {}), ...(consumerInputProps || {}) };
|
|
12
|
+
{
|
|
13
|
+
const extras = ['iconLeft', 'iconRight', 'prefix', 'suffix'].filter((k) => rest[k] != null && rest[k] !== false && rest[k] !== '');
|
|
14
|
+
if (extras.length) warn(`text-field: with type=text-area the master does not draw ${extras.join(', ')} — the contract omits them`);
|
|
15
|
+
delete rest.iconLeft; delete rest.iconRight; delete rest.prefix; delete rest.suffix;
|
|
16
|
+
}
|
|
17
|
+
if (rest.type === 'input') warn('text-field: type=input is NeoTextField, not NeoTextArea');
|
|
18
|
+
if (['input', 'text-area'].includes(rest.type)) delete rest.type;
|
|
19
|
+
const autoId = React.useId();
|
|
20
|
+
const fieldId = id || autoId;
|
|
21
|
+
const helpId = helperText ? `${fieldId}-helper` : undefined;
|
|
22
|
+
const [typed, setTyped] = React.useState(String(defaultValue ?? '').length);
|
|
23
|
+
const count = counter ?? (maxLength != null ? `${value != null ? String(value).length : typed}/${maxLength}` : undefined);
|
|
24
|
+
hasAccessibleName('NeoTextArea', label, rest);
|
|
25
|
+
const keyboardFocus = useKeyboardFocus();
|
|
26
|
+
const helpNode = helpIcon({ text: tooltip, name: tooltipLabel, label, comp: 'TextArea', prefix: 'field' });
|
|
27
|
+
const labelNode = label ? h(FormLabel, { htmlFor: fieldId, required }, label) : null;
|
|
28
|
+
return h(FormControl, { error, disabled, fullWidth: true, variant: 'outlined', onFocus: keyboardFocus.onFocus, onBlur: keyboardFocus.onBlur, onPointerDown: keyboardFocus.onPointerDown, ref },
|
|
29
|
+
label && helpNode ? h('div', { className: 'field__label-row' }, labelNode, helpNode) : labelNode,
|
|
30
|
+
h(OutlinedInput, {
|
|
31
|
+
id: fieldId, notched: false, readOnly,
|
|
32
|
+
multiline: true, ...(rows != null ? { rows } : { minRows, ...(maxRows != null ? { maxRows } : {}) }),
|
|
33
|
+
...(Object.keys(inputAttrs).length ? { inputProps: inputAttrs } : {}),
|
|
34
|
+
'aria-describedby': helpId,
|
|
35
|
+
value, defaultValue, onChange: (e) => { setTyped(e.target.value.length); onChange?.(e); },
|
|
36
|
+
...rest,
|
|
37
|
+
className: [keyboardFocus.active ? 'field--focus' : null, rest.className].filter(Boolean).join(' ') || undefined,
|
|
38
|
+
}),
|
|
39
|
+
(error ? helperText : (helperText || count))
|
|
40
|
+
? h('span', { className: 'field__footer' },
|
|
41
|
+
helperText ? h(FormHelperText, { id: helpId, key: error ? 'error' : 'helper', role: error ? 'alert' : undefined }, helperText) : h('span'),
|
|
42
|
+
count ? h('span', { className: 'field__counter' + (disabled ? ' Mui-disabled' : ''), 'aria-hidden': 'true' }, count) : null)
|
|
43
|
+
: null,
|
|
44
|
+
);
|
|
45
|
+
});
|
package/ui/TextField.mjs
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/TextField.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { FormControl, FormLabel, OutlinedInput, FormHelperText, InputAdornment } from '@mui/material';
|
|
5
|
+
import { renderIcon } from './_icon.mjs';
|
|
6
|
+
import { helpIcon } from './_help.mjs';
|
|
7
|
+
import { useKeyboardFocus } from './_keyboard-focus.mjs';
|
|
8
|
+
import { warn, hasAccessibleName } from './_warn.mjs';
|
|
9
|
+
const h = React.createElement;
|
|
10
|
+
|
|
11
|
+
export const NeoTextField = React.forwardRef(function NeoTextField({ label, required = false, helperText, error = false, readOnly = false, disabled = false, prefix, suffix, tooltip, iconLeft, iconRight, counter, tooltipLabel, startAdornment, endAdornment, id, inputMode, maxLength, inputProps: consumerInputProps, value, defaultValue, onChange, ...rest }, ref) {
|
|
12
|
+
const inputAttrs = { ...(inputMode != null ? { inputMode } : {}), ...(maxLength != null ? { maxLength } : {}), ...(consumerInputProps || {}), onKeyDown: (e) => { rest.onKeyDown?.(e); consumerInputProps?.onKeyDown?.(e); if (e.key === 'Enter' && !e.defaultPrevented && !e.nativeEvent.isComposing && e.keyCode !== 229) e.currentTarget.blur(); } };
|
|
13
|
+
if (rest.type === 'text-area') warn('text-field: type=text-area is NeoTextArea, not NeoTextField');
|
|
14
|
+
if (['input', 'text-area'].includes(rest.type)) delete rest.type;
|
|
15
|
+
const autoId = React.useId();
|
|
16
|
+
const fieldId = id || autoId;
|
|
17
|
+
const helpId = helperText ? `${fieldId}-helper` : undefined;
|
|
18
|
+
const [typed, setTyped] = React.useState(String(defaultValue ?? '').length);
|
|
19
|
+
const count = counter ?? (maxLength != null ? `${value != null ? String(value).length : typed}/${maxLength}` : undefined);
|
|
20
|
+
hasAccessibleName('NeoTextField', label, rest);
|
|
21
|
+
const keyboardFocus = useKeyboardFocus();
|
|
22
|
+
const helpNode = helpIcon({ text: tooltip, name: tooltipLabel, label, comp: 'TextField', prefix: 'field' });
|
|
23
|
+
const adornment = (pos, ...pieces) => {
|
|
24
|
+
const present = pieces.filter(([, n]) => n).map(([k, n]) => h(InputAdornment, { key: k, position: pos }, n));
|
|
25
|
+
if (!present.length) return undefined;
|
|
26
|
+
return present.length === 1 ? present[0] : h(React.Fragment, null, ...present);
|
|
27
|
+
};
|
|
28
|
+
const labelNode = label ? h(FormLabel, { htmlFor: fieldId, required }, label) : null;
|
|
29
|
+
return h(FormControl, { error, disabled, fullWidth: true, variant: 'outlined', onFocus: keyboardFocus.onFocus, onBlur: keyboardFocus.onBlur, onPointerDown: keyboardFocus.onPointerDown, ref },
|
|
30
|
+
label && helpNode ? h('div', { className: 'field__label-row' }, labelNode, helpNode) : labelNode,
|
|
31
|
+
h(OutlinedInput, {
|
|
32
|
+
id: fieldId, notched: false, readOnly,
|
|
33
|
+
startAdornment: startAdornment ?? adornment('start', ['i', iconLeft ? renderIcon(iconLeft, 'field__icon') : null], ['t', prefix ? h('span', { className: 'field__prefix' }, prefix) : null]),
|
|
34
|
+
endAdornment: endAdornment ?? adornment('end', ['t', suffix ? h('span', { className: 'field__suffix' }, suffix) : null], ['i', iconRight ? renderIcon(iconRight, 'field__icon') : null]),
|
|
35
|
+
...(Object.keys(inputAttrs).length ? { inputProps: inputAttrs } : {}),
|
|
36
|
+
'aria-describedby': helpId,
|
|
37
|
+
value, defaultValue, onChange: (e) => { setTyped(e.target.value.length); onChange?.(e); },
|
|
38
|
+
...rest,
|
|
39
|
+
className: [keyboardFocus.active ? 'field--focus' : null, rest.className].filter(Boolean).join(' ') || undefined,
|
|
40
|
+
}),
|
|
41
|
+
(error ? helperText : (helperText || count))
|
|
42
|
+
? h('span', { className: 'field__footer' },
|
|
43
|
+
helperText ? h(FormHelperText, { id: helpId, key: error ? 'error' : 'helper', role: error ? 'alert' : undefined }, helperText) : h('span'),
|
|
44
|
+
count ? h('span', { className: 'field__counter' + (disabled ? ' Mui-disabled' : ''), 'aria-hidden': 'true' }, count) : null)
|
|
45
|
+
: null,
|
|
46
|
+
);
|
|
47
|
+
});
|
package/ui/Toggle.mjs
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/Toggle.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Switch } from '@mui/material';
|
|
5
|
+
import { hasAccessibleName } from './_warn.mjs';
|
|
6
|
+
import { innerInputProps } from './_inner-input.mjs';
|
|
7
|
+
const h = React.createElement;
|
|
8
|
+
|
|
9
|
+
export const NeoToggle = React.forwardRef(function NeoToggle({ label, checked, defaultChecked, disabled = false, onChange, id, 'aria-label': ariaLabel, ...rest }, ref) {
|
|
10
|
+
const autoId = React.useId();
|
|
11
|
+
const fieldId = id || autoId;
|
|
12
|
+
const labelId = `${fieldId}-label`;
|
|
13
|
+
const hasText = label !== undefined && label !== null && String(label).trim() !== '';
|
|
14
|
+
const inputAria = hasText ? { 'aria-labelledby': labelId } : ariaLabel ? { 'aria-label': ariaLabel } : {};
|
|
15
|
+
hasAccessibleName('NeoToggle', label, { ...rest, 'aria-label': ariaLabel });
|
|
16
|
+
return h('span', { className: 'neo-toggle-field', ref },
|
|
17
|
+
hasText ? h('span', { id: labelId }, label) : null,
|
|
18
|
+
h(Switch, {
|
|
19
|
+
id: fieldId, disabled,
|
|
20
|
+
...(checked !== undefined ? { checked } : {}),
|
|
21
|
+
...(defaultChecked !== undefined ? { defaultChecked } : {}),
|
|
22
|
+
...innerInputProps({ role: 'switch', ...inputAria }),
|
|
23
|
+
onChange,
|
|
24
|
+
...rest,
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
});
|
package/ui/Tooltip.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
// packages/mui/ui/Tooltip.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Tooltip } from '@mui/material';
|
|
5
|
+
const h = React.createElement;
|
|
6
|
+
|
|
7
|
+
export const NeoTooltip = React.forwardRef(function NeoTooltip({ placement = 'top', label, ...rest }, ref) {
|
|
8
|
+
const mui = {};
|
|
9
|
+
if (placement === 'none') { mui.arrow = false; }
|
|
10
|
+
if (placement === 'top') { mui.placement = 'top'; }
|
|
11
|
+
if (placement === 'bottom') { mui.placement = 'bottom'; }
|
|
12
|
+
if (placement === 'left') { mui.placement = 'left'; }
|
|
13
|
+
if (placement === 'right') { mui.placement = 'right'; }
|
|
14
|
+
if (placement === 'top-start') { mui.placement = 'top-start'; }
|
|
15
|
+
if (placement === 'top-end') { mui.placement = 'top-end'; }
|
|
16
|
+
if (placement === 'bottom-start') { mui.placement = 'bottom-start'; }
|
|
17
|
+
if (placement === 'bottom-end') { mui.placement = 'bottom-end'; }
|
|
18
|
+
mui.title = label;
|
|
19
|
+
return h(Tooltip, { ...mui, ...rest, ref });
|
|
20
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// packages/mui/ui/_alias-props.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
2
|
+
|
|
3
|
+
export const ALIAS_PROPS = {
|
|
4
|
+
acciones: 'actions',
|
|
5
|
+
cerrar: 'closable',
|
|
6
|
+
onVolver: 'onBack',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const alreadyWarned = new Set();
|
|
10
|
+
|
|
11
|
+
export function withAliases(component, props) {
|
|
12
|
+
let result = props;
|
|
13
|
+
for (const [oldName, newName] of Object.entries(ALIAS_PROPS)) {
|
|
14
|
+
if (!(oldName in props)) continue;
|
|
15
|
+
if (result === props) result = { ...props };
|
|
16
|
+
if (!(newName in result)) result[newName] = result[oldName];
|
|
17
|
+
delete result[oldName];
|
|
18
|
+
const key = `${component}.${oldName}`;
|
|
19
|
+
if (process.env.NODE_ENV !== 'production' && !alreadyWarned.has(key)) {
|
|
20
|
+
alreadyWarned.add(key);
|
|
21
|
+
console.warn(`[Neo] ${component}: the prop \`${oldName}\` is now called \`${newName}\`, since the move to English. `
|
|
22
|
+
+ 'It still works in this version and stops working in the next one.');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// packages/mui/ui/_css-layers.mjs — emitted without comments. The reasoning behind each decision lives in Neo's source repository.
|
|
2
|
+
|
|
3
|
+
export const TOKENS = [
|
|
4
|
+
'packages/foundations/tokens/neo-color.css',
|
|
5
|
+
'packages/foundations/tokens/neo-foundations.css',
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
export const PLAIN_FOR_NOW = [];
|
|
9
|
+
|
|
10
|
+
export const SPRITE = 'packages/foundations/icons/icons.svg';
|
|
11
|
+
|
|
12
|
+
export const REACT_LAYERS = [...TOKENS, ...PLAIN_FOR_NOW];
|
|
13
|
+
|
|
14
|
+
export const CLASSES_WITHOUT_RULE = [
|
|
15
|
+
];
|