@sqrzro/ui 4.0.0-alpha.56 → 4.0.0-alpha.58
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/components/buttons/Button/index.d.ts +2 -1
- package/dist/components/buttons/Button/index.js +2 -2
- package/dist/components/collections/ListItemMeta/index.js +1 -11
- package/dist/components/collections/ListItemSecondary/index.js +7 -3
- package/dist/components/elements/Link/index.d.ts +2 -1
- package/dist/components/elements/Link/index.js +4 -2
- package/dist/components/elements/MenuItem/index.d.ts +3 -1
- package/dist/components/elements/MenuItem/index.js +2 -2
- package/dist/forms/components/FormField/index.d.ts +2 -1
- package/dist/forms/components/FormField/index.js +6 -2
- package/dist/forms/hooks/useForm.d.ts +1 -1
- package/dist/forms/hooks/useForm.js +1 -1
- package/dist/forms/utility/extract-input-props.js +1 -0
- package/package.json +7 -7
|
@@ -19,6 +19,7 @@ export interface ButtonProps extends ClassNameProps<ButtonClassNames> {
|
|
|
19
19
|
readonly id?: string;
|
|
20
20
|
/** If true, the Button will be greyed out and non-interactive */
|
|
21
21
|
readonly isDisabled?: boolean;
|
|
22
|
+
readonly isDownload?: boolean;
|
|
22
23
|
/** If true, the button will stretch to the full width of its container */
|
|
23
24
|
readonly isFullWidth?: boolean;
|
|
24
25
|
/** If true, the Button will show a loading indicator, and be non-interactive */
|
|
@@ -61,5 +62,5 @@ export interface ButtonProps extends ClassNameProps<ButtonClassNames> {
|
|
|
61
62
|
* | ---- | ----------- | ---- |
|
|
62
63
|
* | root | The button element | `StylableButtonClassName` |
|
|
63
64
|
*/
|
|
64
|
-
declare function Button({ children, classNames, classNameProps, form, hasAssistiveLabel, href, icon, id, isDisabled, isFullWidth, isLoading, isNewWindow, isText, name, onClick, popoverTarget, scroll, style, type, value, variant, }: ButtonProps): React.ReactElement;
|
|
65
|
+
declare function Button({ children, classNames, classNameProps, form, hasAssistiveLabel, href, icon, id, isDisabled, isDownload, isFullWidth, isLoading, isNewWindow, isText, name, onClick, popoverTarget, scroll, style, type, value, variant, }: ButtonProps): React.ReactElement;
|
|
65
66
|
export default Button;
|
|
@@ -24,7 +24,7 @@ import Link from '../../elements/Link';
|
|
|
24
24
|
* | ---- | ----------- | ---- |
|
|
25
25
|
* | root | The button element | `StylableButtonClassName` |
|
|
26
26
|
*/
|
|
27
|
-
function Button({ children, classNames, classNameProps, form, hasAssistiveLabel, href, icon, id, isDisabled, isFullWidth, isLoading, isNewWindow, isText, name, onClick, popoverTarget, scroll, style, type, value, variant, }) {
|
|
27
|
+
function Button({ children, classNames, classNameProps, form, hasAssistiveLabel, href, icon, id, isDisabled, isDownload, isFullWidth, isLoading, isNewWindow, isText, name, onClick, popoverTarget, scroll, style, type, value, variant, }) {
|
|
28
28
|
const ref = useRef(null);
|
|
29
29
|
const componentClassNames = useClassNames(isText ? 'textButton' : 'button', { props: { ...classNameProps, ...applyVariants(variant) } }, classNames);
|
|
30
30
|
const className = tw('relative flex cursor-pointer items-center justify-center whitespace-nowrap', isDisabled || isLoading ? 'pointer-events-none opacity-30' : null, isFullWidth ? 'w-full' : null, componentClassNames?.root);
|
|
@@ -33,7 +33,7 @@ function Button({ children, classNames, classNameProps, form, hasAssistiveLabel,
|
|
|
33
33
|
* headless in other applications, and so should have consistency.
|
|
34
34
|
*/
|
|
35
35
|
if (href) {
|
|
36
|
-
return (_jsx(Link, { className: className, href: href, id: id, isNewWindow: isNewWindow, onClick: onClick, scroll: scroll, style: style, children: _jsxs("span", { className: "flex items-center gap-2", children: [icon ? (_jsx("span", { className: tw('flex-none', componentClassNames?.icon), children: icon })) : null, _jsx("span", { className: tw(hasAssistiveLabel ? 'sr-only' : null), children: children })] }) }));
|
|
36
|
+
return (_jsx(Link, { className: className, href: href, id: id, isDownload: isDownload, isNewWindow: isNewWindow, onClick: onClick, scroll: scroll, style: style, children: _jsxs("span", { className: "flex items-center gap-2", children: [icon ? (_jsx("span", { className: tw('flex-none', componentClassNames?.icon), children: icon })) : null, _jsx("span", { className: tw(hasAssistiveLabel ? 'sr-only' : null), children: children })] }) }));
|
|
37
37
|
}
|
|
38
38
|
return (_jsxs("button", { ref: ref, className: className, disabled: isDisabled || isLoading || false, form: form, id: id, name: name, onClick: onClick, popoverTarget: popoverTarget, style: style, type: type === 'submit' ? 'submit' : 'button', value: value, children: [isLoading ? (_jsx("div", { className: "absolute left-1/2 top-1/2 -translate-x-2/4 -translate-y-2/4", children: "\u2022\u2022\u2022" })) : null, _jsxs("span", { className: "flex items-center gap-2", children: [icon ? (_jsx("span", { className: tw('flex-none', componentClassNames?.icon), children: icon })) : null, _jsx("span", { className: tw(hasAssistiveLabel ? 'sr-only' : null, isLoading ? 'text-transparent' : null), children: children })] })] }));
|
|
39
39
|
}
|
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { isValidElement } from 'react';
|
|
4
3
|
import { filterNull } from '@sqrzro/utility';
|
|
5
4
|
import { useClassNames } from '../../../styles/context';
|
|
6
5
|
import isDataTableArray from '../../../utility/is-data-object-array';
|
|
7
6
|
import DataTable from '../DataTable';
|
|
8
|
-
function getKey(item) {
|
|
9
|
-
if (isValidElement(item)) {
|
|
10
|
-
return item.key || '-';
|
|
11
|
-
}
|
|
12
|
-
if (typeof item === 'object') {
|
|
13
|
-
return JSON.stringify(item);
|
|
14
|
-
}
|
|
15
|
-
return String(item);
|
|
16
|
-
}
|
|
17
7
|
function ListItemMeta({ classNameProps, classNames, data, }) {
|
|
18
8
|
const componentClassNames = useClassNames('listItemMeta', { props: classNameProps }, classNames);
|
|
19
9
|
if (!data?.length) {
|
|
@@ -23,6 +13,6 @@ function ListItemMeta({ classNameProps, classNames, data, }) {
|
|
|
23
13
|
if (isDataTableArray(filteredData)) {
|
|
24
14
|
return _jsx(DataTable, { data: filteredData });
|
|
25
15
|
}
|
|
26
|
-
return (_jsx("ul", { className: componentClassNames?.root, children: filteredData.map((item) => (_jsx("li", {
|
|
16
|
+
return (_jsx("ul", { className: componentClassNames?.root, children: filteredData.map((item, index) => (_jsx("li", { children: item }, index))) }));
|
|
27
17
|
}
|
|
28
18
|
export default ListItemMeta;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { isValidElement } from 'react';
|
|
4
3
|
import { useClassNames } from '../../../styles/context';
|
|
5
4
|
import ListItemMeta from '../ListItemMeta';
|
|
5
|
+
function isReactElement(element) {
|
|
6
|
+
return typeof element === 'object' && element !== null && '$$typeof' in element;
|
|
7
|
+
}
|
|
6
8
|
function ListItemSecondary({ classNameProps, classNames, data, }) {
|
|
7
9
|
const componentClassNames = useClassNames('listItem', { props: classNameProps }, classNames);
|
|
8
10
|
if (!data) {
|
|
9
11
|
return null;
|
|
10
12
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
// We have to use a custom check rather than the React.isValidElement type guard, because
|
|
14
|
+
// (seemingly as of Next 16.2) we get a JSON circular error using it.
|
|
15
|
+
if (isReactElement(data)) {
|
|
16
|
+
return data;
|
|
13
17
|
}
|
|
14
18
|
return (_jsxs("div", { className: componentClassNames?.secondary, children: [data.title ? _jsx("p", { className: componentClassNames?.title, children: data.title }) : null, data.description ? (_jsx("div", { className: componentClassNames?.description, children: data.description })) : null, _jsx(ListItemMeta, { classNameProps: { ...classNameProps, isSecondary: true }, data: data.meta })] }));
|
|
15
19
|
}
|
|
@@ -10,10 +10,11 @@ export interface LinkProps extends ClassNameProps<LinkClassNames>, DataProps {
|
|
|
10
10
|
children: React.ReactNode;
|
|
11
11
|
href?: string | null;
|
|
12
12
|
id?: string;
|
|
13
|
+
isDownload?: boolean;
|
|
13
14
|
isNewWindow?: boolean;
|
|
14
15
|
onClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
|
15
16
|
scroll?: boolean;
|
|
16
17
|
style?: React.CSSProperties;
|
|
17
18
|
}
|
|
18
|
-
declare function Link({ children, className, classNames, classNameProps, isNewWindow, href, id, onClick, scroll, style, ...dataProps }: Readonly<LinkProps>): React.ReactElement;
|
|
19
|
+
declare function Link({ children, className, classNames, classNameProps, isDownload, isNewWindow, href, id, onClick, scroll, style, ...dataProps }: Readonly<LinkProps>): React.ReactElement;
|
|
19
20
|
export default Link;
|
|
@@ -10,14 +10,16 @@ function normalizeHref(href) {
|
|
|
10
10
|
}
|
|
11
11
|
return joinURL(href);
|
|
12
12
|
}
|
|
13
|
-
function Link({ children, className, classNames, classNameProps, isNewWindow, href = '/', id, onClick, scroll, style, ...dataProps }) {
|
|
13
|
+
function Link({ children, className, classNames, classNameProps, isDownload, isNewWindow, href = '/', id, onClick, scroll, style, ...dataProps }) {
|
|
14
14
|
const componentClassNames = useClassNames('link', { props: classNameProps }, classNames);
|
|
15
|
+
const downloadProps = isDownload ? { download: true } : {};
|
|
15
16
|
const newWindowProps = isNewWindow
|
|
16
17
|
? { rel: 'noopener noreferrer', target: '_blank' }
|
|
17
18
|
: {};
|
|
18
19
|
const props = {
|
|
19
|
-
...newWindowProps,
|
|
20
20
|
...dataProps,
|
|
21
|
+
...downloadProps,
|
|
22
|
+
...newWindowProps,
|
|
21
23
|
className: tw(componentClassNames?.root, className),
|
|
22
24
|
id,
|
|
23
25
|
onClick,
|
|
@@ -2,6 +2,8 @@ import type { ClassNameProps } from '../../../styles/classnames/interfaces';
|
|
|
2
2
|
import type { ActionComponentProps } from '../../utility/Action';
|
|
3
3
|
import type { MenuClassNames } from '../Menu';
|
|
4
4
|
interface MenuItemProps<Data, Response> extends ClassNameProps<MenuClassNames>, ActionComponentProps<Data, Response> {
|
|
5
|
+
isDownload?: boolean;
|
|
6
|
+
isNewWindow?: boolean;
|
|
5
7
|
}
|
|
6
|
-
declare function MenuItem<Data, Response>({ children, classNameProps, classNames, href, isLoading, onClick, }: MenuItemProps<Data, Response>): React.ReactElement;
|
|
8
|
+
declare function MenuItem<Data, Response>({ children, classNameProps, classNames, href, isDownload, isLoading, isNewWindow, onClick, }: MenuItemProps<Data, Response>): React.ReactElement;
|
|
7
9
|
export default MenuItem;
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useClassNames } from '../../../styles/context';
|
|
4
4
|
import Link from '../Link';
|
|
5
|
-
function MenuItem({ children, classNameProps, classNames, href, isLoading, onClick, }) {
|
|
5
|
+
function MenuItem({ children, classNameProps, classNames, href, isDownload, isLoading, isNewWindow, onClick, }) {
|
|
6
6
|
const componentClassNames = useClassNames('menu', { props: classNameProps }, classNames);
|
|
7
7
|
if (href) {
|
|
8
|
-
return (_jsx(Link, { className: componentClassNames?.link, href: href, onClick: onClick, children: children }));
|
|
8
|
+
return (_jsx(Link, { className: componentClassNames?.link, href: href, isDownload: isDownload, isNewWindow: isNewWindow, onClick: onClick, children: children }));
|
|
9
9
|
}
|
|
10
10
|
return (_jsxs("button", { className: componentClassNames?.link, onClick: onClick, type: "button", children: [children, " ", isLoading && '...'] }));
|
|
11
11
|
}
|
|
@@ -20,7 +20,8 @@ export interface FormFieldProps<T, V extends T> extends ClassNameProps<FormField
|
|
|
20
20
|
isOptional?: boolean;
|
|
21
21
|
label?: React.ReactNode;
|
|
22
22
|
onChange?: (event: InputEvent<T>) => void;
|
|
23
|
+
onFieldChange?: (event: InputEvent<T>) => void;
|
|
23
24
|
render: (props: InputProps<T>) => React.ReactElement | null;
|
|
24
25
|
}
|
|
25
|
-
declare function FormField<T, V extends T>({ action, classNameProps, classNames, details, error, hasAssistiveError, hasAssistiveDetails, hasAssistiveLabel, id, isContentOnly, isDisabled, isOptional, label, name, onChange, onKeyDown, render, value, }: Readonly<FormFieldProps<T, V>>): React.ReactElement | null;
|
|
26
|
+
declare function FormField<T, V extends T>({ action, classNameProps, classNames, details, error, hasAssistiveError, hasAssistiveDetails, hasAssistiveLabel, id, isContentOnly, isDisabled, isOptional, label, name, onChange, onFieldChange, onKeyDown, render, value, }: Readonly<FormFieldProps<T, V>>): React.ReactElement | null;
|
|
26
27
|
export default FormField;
|
|
@@ -11,10 +11,14 @@ function checkHasError(error) {
|
|
|
11
11
|
Object.keys(error).length > 0 &&
|
|
12
12
|
Object.values(error).some((item) => typeof item === 'string'));
|
|
13
13
|
}
|
|
14
|
-
function FormField({ action, classNameProps, classNames, details, error, hasAssistiveError, hasAssistiveDetails, hasAssistiveLabel, id, isContentOnly, isDisabled, isOptional, label, name, onChange, onKeyDown, render, value, }) {
|
|
14
|
+
function FormField({ action, classNameProps, classNames, details, error, hasAssistiveError, hasAssistiveDetails, hasAssistiveLabel, id, isContentOnly, isDisabled, isOptional, label, name, onChange, onFieldChange, onKeyDown, render, value, }) {
|
|
15
15
|
const componentClassNames = useClassNames('formField', { props: classNameProps, states: { isError: checkHasError(error) } }, classNames);
|
|
16
16
|
const inputId = id || `ff_${name}`;
|
|
17
17
|
const [inputError, setInputError] = useState(null);
|
|
18
|
+
function handleChange(event) {
|
|
19
|
+
onFieldChange?.(event);
|
|
20
|
+
onChange?.(event);
|
|
21
|
+
}
|
|
18
22
|
function handleError(message) {
|
|
19
23
|
setInputError(message);
|
|
20
24
|
}
|
|
@@ -28,7 +32,7 @@ function FormField({ action, classNameProps, classNames, details, error, hasAssi
|
|
|
28
32
|
isOptional,
|
|
29
33
|
label,
|
|
30
34
|
name,
|
|
31
|
-
onChange,
|
|
35
|
+
onChange: handleChange,
|
|
32
36
|
onError: handleError,
|
|
33
37
|
onKeyDown,
|
|
34
38
|
value,
|
|
@@ -12,7 +12,7 @@ export interface FieldPropsReturn<Request, K extends keyof Request = keyof Reque
|
|
|
12
12
|
isDisabled?: boolean;
|
|
13
13
|
label?: string;
|
|
14
14
|
name: string;
|
|
15
|
-
|
|
15
|
+
onFieldChange: (event: InputEvent<Request[K]>) => void;
|
|
16
16
|
value: Request[K];
|
|
17
17
|
}
|
|
18
18
|
export interface ToastsArgs {
|
|
@@ -173,7 +173,7 @@ function useForm({ defaults = {}, onError, onSubmit, onSuccess, onValidation, re
|
|
|
173
173
|
error: getErrorsForField(errors, name),
|
|
174
174
|
label: getLabel(name, label),
|
|
175
175
|
name: name,
|
|
176
|
-
|
|
176
|
+
onFieldChange: handleChange,
|
|
177
177
|
value: data[name],
|
|
178
178
|
};
|
|
179
179
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqrzro/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.0.0-alpha.
|
|
4
|
+
"version": "4.0.0-alpha.58",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"license": "ISC",
|
|
@@ -52,18 +52,18 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"clsx": "^2.1.1",
|
|
54
54
|
"drizzle-orm": "^0.45.1",
|
|
55
|
-
"next": "^16.
|
|
55
|
+
"next": "^16.2.0",
|
|
56
56
|
"react": "^19.2.4",
|
|
57
57
|
"react-dom": "^19.2.4",
|
|
58
58
|
"tailwind-merge": "^3.5.0",
|
|
59
59
|
"use-deep-compare-effect": "^1.8.1",
|
|
60
|
-
"@sqrzro/addons": "^4.0.0-alpha.
|
|
60
|
+
"@sqrzro/addons": "^4.0.0-alpha.10",
|
|
61
61
|
"@sqrzro/utility": "^4.0.0-alpha.19"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@storybook/addon-a11y": "^10.
|
|
65
|
-
"@storybook/addon-docs": "^10.
|
|
66
|
-
"@storybook/nextjs": "^10.
|
|
64
|
+
"@storybook/addon-a11y": "^10.3.1",
|
|
65
|
+
"@storybook/addon-docs": "^10.3.1",
|
|
66
|
+
"@storybook/nextjs": "^10.3.1",
|
|
67
67
|
"@testing-library/react": "^16.3.2",
|
|
68
68
|
"@testing-library/user-event": "^14.6.1",
|
|
69
69
|
"@types/react": "^19.2.14",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"cpx": "^1.5.0",
|
|
73
73
|
"next-router-mock": "^1.0.5",
|
|
74
74
|
"rimraf": "^6.1.3",
|
|
75
|
-
"storybook": "^10.
|
|
75
|
+
"storybook": "^10.3.1",
|
|
76
76
|
"tsc-alias": "^1.8.16",
|
|
77
77
|
"tslib": "^2.8.1",
|
|
78
78
|
"typescript": "^5.9.3"
|