@surflane/ui 0.1.0 → 0.3.0
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/README.md +8 -8
- package/dist/advanced-forms.d.ts +2 -2
- package/dist/advanced-forms.js +39 -39
- package/dist/app-shell.d.ts +3 -2
- package/dist/app-shell.js +29 -13
- package/dist/button.d.ts +5 -5
- package/dist/button.js +12 -12
- package/dist/chart.d.ts +3 -3
- package/dist/chart.js +36 -34
- package/dist/commands.d.ts +5 -5
- package/dist/commands.js +17 -17
- package/dist/content.d.ts +2 -2
- package/dist/content.js +66 -28
- package/dist/context-overlays.d.ts +3 -3
- package/dist/context-overlays.js +11 -11
- package/dist/data.d.ts +58 -13
- package/dist/data.js +63 -21
- package/dist/date-picker.d.ts +8 -8
- package/dist/date-picker.js +18 -18
- package/dist/dialog-focus.js +7 -7
- package/dist/dialog.d.ts +5 -5
- package/dist/dialog.js +16 -16
- package/dist/feedback.d.ts +5 -5
- package/dist/feedback.js +20 -20
- package/dist/file-upload.d.ts +9 -9
- package/dist/file-upload.js +64 -63
- package/dist/filter-bar.d.ts +1 -1
- package/dist/filter-bar.js +9 -9
- package/dist/form-state.d.ts +1 -1
- package/dist/form-state.js +11 -11
- package/dist/forms.d.ts +5 -5
- package/dist/forms.js +32 -32
- package/dist/foundations.d.ts +10 -10
- package/dist/foundations.js +26 -26
- package/dist/icon.d.ts +8 -7
- package/dist/icon.js +23 -22
- package/dist/index.d.ts +38 -37
- package/dist/index.js +39 -38
- package/dist/input-extensions.d.ts +3 -3
- package/dist/input-extensions.js +21 -21
- package/dist/input.d.ts +2 -2
- package/dist/input.js +7 -7
- package/dist/markdown.d.ts +2 -2
- package/dist/markdown.js +38 -38
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +19 -19
- package/dist/messages.d.ts +2 -2
- package/dist/messages.js +18 -18
- package/dist/navigation-menu.d.ts +2 -2
- package/dist/navigation-menu.js +16 -16
- package/dist/navigation.d.ts +5 -5
- package/dist/navigation.js +40 -40
- package/dist/otp-field.d.ts +1 -1
- package/dist/otp-field.js +14 -14
- package/dist/pagination.d.ts +2 -2
- package/dist/pagination.js +12 -12
- package/dist/panels.d.ts +8 -8
- package/dist/panels.js +39 -39
- package/dist/parts.d.ts +2 -2
- package/dist/parts.js +8 -8
- package/dist/popover.d.ts +3 -3
- package/dist/popover.js +6 -6
- package/dist/scroll-area.d.ts +1 -1
- package/dist/scroll-area.js +20 -20
- package/dist/select.d.ts +8 -8
- package/dist/select.js +21 -21
- package/dist/sizing.d.ts +3 -3
- package/dist/sizing.js +9 -9
- package/dist/slot.d.ts +1 -1
- package/dist/slot.js +4 -4
- package/dist/status.d.ts +4 -4
- package/dist/status.js +8 -8
- package/dist/syntax-engine.js +43 -43
- package/dist/syntax-highlighter.d.ts +2 -2
- package/dist/syntax-highlighter.js +32 -32
- package/dist/table-selection.d.ts +31 -0
- package/dist/table-selection.js +50 -0
- package/dist/toast.d.ts +4 -4
- package/dist/toast.js +33 -33
- package/dist/tone-transition.d.ts +1 -1
- package/dist/tone-transition.js +17 -17
- package/dist/tooltip.d.ts +1 -1
- package/dist/tooltip.js +4 -4
- package/dist/tree.d.ts +4 -4
- package/dist/tree.js +60 -60
- package/package.json +2 -2
- package/src/styles.css +266 -142
package/dist/dialog.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import * as React from
|
|
4
|
-
import { useDialogFocusBoundary } from
|
|
5
|
-
import { useComponentSize } from
|
|
6
|
-
import { Dialog as BaseDialog } from
|
|
7
|
-
import { AlertDialog as BaseAlertDialog } from
|
|
8
|
-
const cx = (...v) => v.filter(Boolean).join(
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { useDialogFocusBoundary } from './dialog-focus.js';
|
|
5
|
+
import { useComponentSize } from './sizing.js';
|
|
6
|
+
import { Dialog as BaseDialog } from '@base-ui/react/dialog';
|
|
7
|
+
import { AlertDialog as BaseAlertDialog } from '@base-ui/react/alert-dialog';
|
|
8
|
+
const cx = (...v) => v.filter(Boolean).join(' ');
|
|
9
9
|
/** Visible icon close action; inherits Dialog focus and dismissal behavior. */
|
|
10
|
-
const DialogCloseButton = React.forwardRef(function DialogCloseButton({ className,
|
|
11
|
-
return (_jsx(BaseDialog.Close, { ...props, ref: ref, "aria-label": label, className: cx(
|
|
10
|
+
const DialogCloseButton = React.forwardRef(function DialogCloseButton({ className, 'aria-label': label = 'Close dialog', ...props }, ref) {
|
|
11
|
+
return (_jsx(BaseDialog.Close, { ...props, ref: ref, "aria-label": label, className: cx('pui-icon-button pui-dialog-close', typeof className === 'string' && className), children: _jsx("svg", { width: '18', height: '18', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '1.7', "aria-hidden": 'true', children: _jsx("path", { d: 'm6 6 12 12M18 6 6 18' }) }) }));
|
|
12
12
|
});
|
|
13
|
-
const DialogContent = React.forwardRef(function DialogContent({ className, children, closeLabel =
|
|
13
|
+
const DialogContent = React.forwardRef(function DialogContent({ className, children, closeLabel = 'Close dialog', closeDisabled, focusBoundaryLabel = 'Continue dialog navigation', ...props }, ref) {
|
|
14
14
|
const scheme = useComponentSize();
|
|
15
15
|
const portalRef = useDialogFocusBoundary(focusBoundaryLabel);
|
|
16
|
-
return (_jsxs(BaseDialog.Portal, { ref: portalRef, children: [_jsx(BaseDialog.Backdrop, { className:
|
|
16
|
+
return (_jsxs(BaseDialog.Portal, { ref: portalRef, children: [_jsx(BaseDialog.Backdrop, { className: 'pui-dialog-backdrop' }), _jsxs(BaseDialog.Popup, { "data-ui-size": scheme, ...props, ref: ref, className: cx('pui-dialog-popup pui-dialog-content', typeof className === 'string' && className), children: [_jsx(DialogCloseButton, { "aria-label": closeLabel, disabled: closeDisabled }), children] })] }));
|
|
17
17
|
});
|
|
18
18
|
function DialogHeader({ className, ...props }) {
|
|
19
|
-
return _jsx("div", { ...props, className: cx(
|
|
19
|
+
return _jsx("div", { ...props, className: cx('pui-dialog-header', className) });
|
|
20
20
|
}
|
|
21
21
|
const DialogBody = React.forwardRef(function DialogBody({ className, ...props }, ref) {
|
|
22
|
-
return (_jsx("div", { ...props, ref: ref, className: cx(
|
|
22
|
+
return (_jsx("div", { ...props, ref: ref, className: cx('pui-dialog-body', className) }));
|
|
23
23
|
});
|
|
24
24
|
function DialogFooter({ className, ...props }) {
|
|
25
|
-
return _jsx("div", { ...props, className: cx(
|
|
25
|
+
return _jsx("div", { ...props, className: cx('pui-dialog-footer', className) });
|
|
26
26
|
}
|
|
27
27
|
export const Dialog = {
|
|
28
28
|
...BaseDialog,
|
|
@@ -32,10 +32,10 @@ export const Dialog = {
|
|
|
32
32
|
Footer: DialogFooter,
|
|
33
33
|
CloseButton: DialogCloseButton,
|
|
34
34
|
};
|
|
35
|
-
const AlertDialogContent = React.forwardRef(function AlertDialogContent({ className, focusBoundaryLabel =
|
|
35
|
+
const AlertDialogContent = React.forwardRef(function AlertDialogContent({ className, focusBoundaryLabel = 'Continue dialog navigation', ...props }, ref) {
|
|
36
36
|
const scheme = useComponentSize();
|
|
37
37
|
const portalRef = useDialogFocusBoundary(focusBoundaryLabel);
|
|
38
|
-
return (_jsxs(BaseAlertDialog.Portal, { ref: portalRef, children: [_jsx(BaseAlertDialog.Backdrop, { className:
|
|
38
|
+
return (_jsxs(BaseAlertDialog.Portal, { ref: portalRef, children: [_jsx(BaseAlertDialog.Backdrop, { className: 'pui-dialog-backdrop' }), _jsx(BaseAlertDialog.Popup, { "data-ui-size": scheme, ...props, ref: ref, className: cx('pui-dialog-popup pui-dialog-content pui-confirm-content', typeof className === 'string' && className) })] }));
|
|
39
39
|
});
|
|
40
40
|
export const AlertDialog = {
|
|
41
41
|
...BaseAlertDialog,
|
package/dist/feedback.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import { type ComponentSizeAlias } from
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type ComponentSizeAlias } from './sizing.js';
|
|
3
3
|
export declare const Progress: {
|
|
4
4
|
Root: React.ForwardRefExoticComponent<Omit<Omit<Omit<import("@base-ui/react/progress").ProgressRootProps, "ref"> & React.RefAttributes<HTMLDivElement>, "ref">, "size"> & {
|
|
5
5
|
size?: import("./sizing.js").ComponentSize;
|
|
@@ -34,7 +34,7 @@ export declare const Meter: {
|
|
|
34
34
|
size?: import("./sizing.js").ComponentSize;
|
|
35
35
|
} & React.RefAttributes<HTMLDivElement>>;
|
|
36
36
|
};
|
|
37
|
-
export type SpinnerProps = Omit<React.ComponentPropsWithoutRef<
|
|
37
|
+
export type SpinnerProps = Omit<React.ComponentPropsWithoutRef<'span'>, 'children'> & {
|
|
38
38
|
label?: string;
|
|
39
39
|
decorative?: boolean;
|
|
40
40
|
size?: ComponentSizeAlias;
|
|
@@ -45,8 +45,8 @@ export declare const Spinner: React.ForwardRefExoticComponent<Omit<Omit<React.De
|
|
|
45
45
|
decorative?: boolean;
|
|
46
46
|
size?: ComponentSizeAlias;
|
|
47
47
|
} & React.RefAttributes<HTMLSpanElement>>;
|
|
48
|
-
export type SkeletonProps = Omit<React.ComponentPropsWithoutRef<
|
|
49
|
-
shape?:
|
|
48
|
+
export type SkeletonProps = Omit<React.ComponentPropsWithoutRef<'span'>, 'children'> & {
|
|
49
|
+
shape?: 'text' | 'block' | 'circle';
|
|
50
50
|
};
|
|
51
51
|
/** Decorative placeholder; the owning region provides aria-busy and loading text. */
|
|
52
52
|
export declare const Skeleton: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref">, "children"> & {
|
package/dist/feedback.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import * as React from
|
|
4
|
-
import { useComponentSize } from
|
|
5
|
-
import { Progress as BaseProgress } from
|
|
6
|
-
import { Meter as BaseMeter } from
|
|
7
|
-
import { part } from
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { useComponentSize } from './sizing.js';
|
|
5
|
+
import { Progress as BaseProgress } from '@base-ui/react/progress';
|
|
6
|
+
import { Meter as BaseMeter } from '@base-ui/react/meter';
|
|
7
|
+
import { part } from './parts.js';
|
|
8
8
|
export const Progress = {
|
|
9
9
|
...BaseProgress,
|
|
10
|
-
Root: part(BaseProgress.Root,
|
|
11
|
-
Label: part(BaseProgress.Label,
|
|
12
|
-
Value: part(BaseProgress.Value,
|
|
13
|
-
Track: part(BaseProgress.Track,
|
|
14
|
-
Indicator: part(BaseProgress.Indicator,
|
|
10
|
+
Root: part(BaseProgress.Root, 'pui-gauge pui-progress'),
|
|
11
|
+
Label: part(BaseProgress.Label, 'pui-gauge-label'),
|
|
12
|
+
Value: part(BaseProgress.Value, 'pui-gauge-value'),
|
|
13
|
+
Track: part(BaseProgress.Track, 'pui-gauge-track'),
|
|
14
|
+
Indicator: part(BaseProgress.Indicator, 'pui-gauge-indicator'),
|
|
15
15
|
};
|
|
16
16
|
export const Meter = {
|
|
17
17
|
...BaseMeter,
|
|
18
|
-
Root: part(BaseMeter.Root,
|
|
19
|
-
Label: part(BaseMeter.Label,
|
|
20
|
-
Value: part(BaseMeter.Value,
|
|
21
|
-
Track: part(BaseMeter.Track,
|
|
22
|
-
Indicator: part(BaseMeter.Indicator,
|
|
18
|
+
Root: part(BaseMeter.Root, 'pui-gauge pui-meter'),
|
|
19
|
+
Label: part(BaseMeter.Label, 'pui-gauge-label'),
|
|
20
|
+
Value: part(BaseMeter.Value, 'pui-gauge-value'),
|
|
21
|
+
Track: part(BaseMeter.Track, 'pui-gauge-track'),
|
|
22
|
+
Indicator: part(BaseMeter.Indicator, 'pui-gauge-indicator'),
|
|
23
23
|
};
|
|
24
24
|
/** Use decorative within a loading region that already owns the announcement. */
|
|
25
|
-
export const Spinner = React.forwardRef(function Spinner({ label =
|
|
25
|
+
export const Spinner = React.forwardRef(function Spinner({ label = 'Loading', decorative = false, size, className, ...props }, ref) {
|
|
26
26
|
const scheme = useComponentSize(size);
|
|
27
|
-
return (_jsxs("span", { ...props, ref: ref, className: [
|
|
27
|
+
return (_jsxs("span", { ...props, ref: ref, className: ['pui-spinner', className].filter(Boolean).join(' '), "data-size": size, "data-ui-size": scheme, role: decorative ? undefined : 'status', "aria-hidden": decorative || undefined, children: [_jsxs("svg", { viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', "aria-hidden": 'true', children: [_jsx("circle", { cx: '12', cy: '12', r: '9', opacity: '.2' }), _jsx("path", { d: 'M12 3a9 9 0 0 1 9 9', strokeLinecap: 'round' })] }), !decorative && _jsx("span", { className: 'pui-feedback-sr', children: label })] }));
|
|
28
28
|
});
|
|
29
29
|
/** Decorative placeholder; the owning region provides aria-busy and loading text. */
|
|
30
|
-
export const Skeleton = React.forwardRef(function Skeleton({ shape =
|
|
31
|
-
return (_jsx("span", { ...props, ref: ref, className: [
|
|
30
|
+
export const Skeleton = React.forwardRef(function Skeleton({ shape = 'text', className, ...props }, ref) {
|
|
31
|
+
return (_jsx("span", { ...props, ref: ref, className: ['pui-skeleton', className].filter(Boolean).join(' '), "data-shape": shape, "aria-hidden": 'true' }));
|
|
32
32
|
});
|
|
33
33
|
// Flat names for React Server Components, which cannot dot into client exports.
|
|
34
34
|
export const { Indicator: MeterIndicator, Label: MeterLabel, Root: MeterRoot, Track: MeterTrack, Value: MeterValue, } = Meter;
|
package/dist/file-upload.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import { Field } from
|
|
3
|
-
export type UploadState =
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Field } from './forms.js';
|
|
3
|
+
export type UploadState = 'ready' | 'uploading' | 'success' | 'error' | 'canceled';
|
|
4
4
|
export type UploadTransport = (file: File, context: {
|
|
5
5
|
signal: AbortSignal;
|
|
6
6
|
onProgress: (percent: number | null) => void;
|
|
7
7
|
}) => Promise<void>;
|
|
8
8
|
export type FileRejection = {
|
|
9
9
|
file: File;
|
|
10
|
-
reason:
|
|
10
|
+
reason: 'type' | 'size' | 'count' | 'duplicate';
|
|
11
11
|
};
|
|
12
12
|
export type FileChangeDetails = {
|
|
13
|
-
reason:
|
|
13
|
+
reason: 'select' | 'drop' | 'remove';
|
|
14
14
|
isCanceled: boolean;
|
|
15
15
|
cancel: () => void;
|
|
16
16
|
};
|
|
@@ -30,12 +30,12 @@ export type FileUploadLabels = {
|
|
|
30
30
|
reset: string;
|
|
31
31
|
progress: (file: File) => string;
|
|
32
32
|
};
|
|
33
|
-
export type FileUploadProps = Omit<React.ComponentPropsWithoutRef<typeof Field.Root>,
|
|
33
|
+
export type FileUploadProps = Omit<React.ComponentPropsWithoutRef<typeof Field.Root>, 'children' | 'name' | 'onDragEnter' | 'onDragLeave' | 'onDragOver' | 'onDrop'> & Pick<React.HTMLAttributes<HTMLDivElement>, 'onDragEnter' | 'onDragLeave' | 'onDragOver' | 'onDrop'> & {
|
|
34
34
|
label: React.ReactNode;
|
|
35
35
|
description?: React.ReactNode;
|
|
36
36
|
name?: string;
|
|
37
37
|
form?: string;
|
|
38
|
-
capture?: React.InputHTMLAttributes<HTMLInputElement>[
|
|
38
|
+
capture?: React.InputHTMLAttributes<HTMLInputElement>['capture'];
|
|
39
39
|
accept?: string;
|
|
40
40
|
multiple?: boolean;
|
|
41
41
|
required?: boolean;
|
|
@@ -47,8 +47,8 @@ export type FileUploadProps = Omit<React.ComponentPropsWithoutRef<typeof Field.R
|
|
|
47
47
|
onReject?: (rejections: FileRejection[]) => void;
|
|
48
48
|
transport?: UploadTransport;
|
|
49
49
|
getUploadError?: (error: unknown) => string;
|
|
50
|
-
labels?: Partial<Omit<FileUploadLabels,
|
|
51
|
-
status?: Partial<FileUploadLabels[
|
|
50
|
+
labels?: Partial<Omit<FileUploadLabels, 'status'>> & {
|
|
51
|
+
status?: Partial<FileUploadLabels['status']>;
|
|
52
52
|
};
|
|
53
53
|
};
|
|
54
54
|
/** Native selection/validation and a cancellable transport adapter; no network request is built in. */
|
package/dist/file-upload.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import * as React from
|
|
4
|
-
import { Field } from
|
|
5
|
-
import { Button } from
|
|
6
|
-
import { Progress } from
|
|
7
|
-
import { StatusBadge } from
|
|
8
|
-
import { skin } from
|
|
9
|
-
import { useResetValue, useRootRef } from
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Field } from './forms.js';
|
|
5
|
+
import { Button } from './button.js';
|
|
6
|
+
import { Progress } from './feedback.js';
|
|
7
|
+
import { StatusBadge } from './status.js';
|
|
8
|
+
import { skin } from './parts.js';
|
|
9
|
+
import { useResetValue, useRootRef } from './form-state.js';
|
|
10
10
|
const labelsDefault = {
|
|
11
|
-
choose:
|
|
12
|
-
drop:
|
|
13
|
-
upload:
|
|
14
|
-
retry:
|
|
15
|
-
cancel:
|
|
16
|
-
remove:
|
|
11
|
+
choose: 'Choose files',
|
|
12
|
+
drop: 'or drop files here',
|
|
13
|
+
upload: 'Upload',
|
|
14
|
+
retry: 'Retry',
|
|
15
|
+
cancel: 'Cancel',
|
|
16
|
+
remove: 'Remove',
|
|
17
17
|
status: {
|
|
18
|
-
ready:
|
|
19
|
-
uploading:
|
|
20
|
-
success:
|
|
21
|
-
error:
|
|
22
|
-
canceled:
|
|
18
|
+
ready: 'Ready',
|
|
19
|
+
uploading: 'Uploading',
|
|
20
|
+
success: 'Uploaded',
|
|
21
|
+
error: 'Failed',
|
|
22
|
+
canceled: 'Canceled',
|
|
23
23
|
},
|
|
24
|
-
rejection: ({ file, reason }) => `${file.name}: ${reason ===
|
|
25
|
-
changed: (count) => `${count} file${count === 1 ?
|
|
24
|
+
rejection: ({ file, reason }) => `${file.name}: ${reason === 'type' ? 'file type is not accepted' : reason === 'size' ? 'file is too large' : reason === 'count' ? 'file limit reached' : 'file is already selected'}.`,
|
|
25
|
+
changed: (count) => `${count} file${count === 1 ? '' : 's'} selected.`,
|
|
26
26
|
complete: (file) => `${file.name}: upload complete.`,
|
|
27
27
|
canceled: (file) => `${file.name}: upload canceled.`,
|
|
28
28
|
failed: (file) => `${file.name}: upload failed.`,
|
|
29
|
-
reset:
|
|
29
|
+
reset: 'Files reset.',
|
|
30
30
|
progress: (file) => `${file.name} upload progress`,
|
|
31
31
|
};
|
|
32
32
|
function signature(file) {
|
|
@@ -34,22 +34,22 @@ function signature(file) {
|
|
|
34
34
|
}
|
|
35
35
|
function accepts(file, accept) {
|
|
36
36
|
const rules = accept
|
|
37
|
-
.split(
|
|
37
|
+
.split(',')
|
|
38
38
|
.map((rule) => rule.trim().toLowerCase())
|
|
39
39
|
.filter(Boolean);
|
|
40
40
|
return (!rules.length ||
|
|
41
|
-
rules.some((rule) => rule.startsWith(
|
|
41
|
+
rules.some((rule) => rule.startsWith('.')
|
|
42
42
|
? file.name.toLowerCase().endsWith(rule)
|
|
43
|
-
: rule.endsWith(
|
|
43
|
+
: rule.endsWith('/*')
|
|
44
44
|
? file.type.toLowerCase().startsWith(rule.slice(0, -1))
|
|
45
45
|
: file.type.toLowerCase() === rule));
|
|
46
46
|
}
|
|
47
47
|
function sizeLabel(bytes) {
|
|
48
|
-
const unit = bytes >= 1024 * 1024 ?
|
|
49
|
-
return `${new Intl.NumberFormat(undefined, { maximumFractionDigits: 1 }).format(bytes / (unit ===
|
|
48
|
+
const unit = bytes >= 1024 * 1024 ? 'MB' : bytes >= 1024 ? 'KB' : 'B';
|
|
49
|
+
return `${new Intl.NumberFormat(undefined, { maximumFractionDigits: 1 }).format(bytes / (unit === 'MB' ? 1024 * 1024 : unit === 'KB' ? 1024 : 1))} ${unit}`;
|
|
50
50
|
}
|
|
51
51
|
/** Native selection/validation and a cancellable transport adapter; no network request is built in. */
|
|
52
|
-
export const FileUpload = React.forwardRef(function FileUpload({ label, description, name, form, accept =
|
|
52
|
+
export const FileUpload = React.forwardRef(function FileUpload({ label, description, name, form, accept = '', capture, multiple = false, required = false, maxFiles = Infinity, maxFileSize = Infinity, files: controlled, defaultFiles = [], onFilesChange, onReject, transport, getUploadError, labels: customLabels, disabled = false, className, onDragEnter, onDragLeave, onDragOver, onDrop, ...props }, forwardedRef) {
|
|
53
53
|
const labels = {
|
|
54
54
|
...labelsDefault,
|
|
55
55
|
...customLabels,
|
|
@@ -57,7 +57,7 @@ export const FileUpload = React.forwardRef(function FileUpload({ label, descript
|
|
|
57
57
|
};
|
|
58
58
|
const [root, ref] = useRootRef(forwardedRef), input = React.useRef(null);
|
|
59
59
|
const uid = React.useId(), inputId = `${uid}-input`, errorId = `${uid}-selection-errors`;
|
|
60
|
-
const [metadata, setMetadata] = React.useState(new Map()), [rejections, setRejections] = React.useState([]), [announcement, announce] = React.useState(
|
|
60
|
+
const [metadata, setMetadata] = React.useState(new Map()), [rejections, setRejections] = React.useState([]), [announcement, announce] = React.useState(''), [dragging, setDragging] = React.useState(false);
|
|
61
61
|
const notifying = React.useRef(false), lastSynced = React.useRef(null), skipNotifyFor = React.useRef(null);
|
|
62
62
|
const primaryButtons = React.useRef(new WeakMap()), removeButtons = React.useRef(new WeakMap()), focusRemove = React.useRef(null), focusPicker = React.useRef(null);
|
|
63
63
|
const requests = React.useRef(new Map()), mounted = React.useRef(false), dragDepth = React.useRef(0);
|
|
@@ -93,7 +93,7 @@ export const FileUpload = React.forwardRef(function FileUpload({ label, descript
|
|
|
93
93
|
input.current) {
|
|
94
94
|
notifying.current = true;
|
|
95
95
|
try {
|
|
96
|
-
input.current.dispatchEvent(new Event(
|
|
96
|
+
input.current.dispatchEvent(new Event('change', { bubbles: true }));
|
|
97
97
|
}
|
|
98
98
|
finally {
|
|
99
99
|
notifying.current = false;
|
|
@@ -142,11 +142,12 @@ export const FileUpload = React.forwardRef(function FileUpload({ label, descript
|
|
|
142
142
|
return;
|
|
143
143
|
requests.current.delete(file);
|
|
144
144
|
request.controller.abort();
|
|
145
|
-
update(file, { status:
|
|
145
|
+
update(file, { status: 'canceled', progress: null });
|
|
146
146
|
announce(labels.canceled(file));
|
|
147
147
|
}
|
|
148
148
|
React.useEffect(() => {
|
|
149
149
|
if (disabled) {
|
|
150
|
+
;
|
|
150
151
|
[...requests.current.keys()].forEach(cancel);
|
|
151
152
|
dragDepth.current = 0;
|
|
152
153
|
setDragging(false);
|
|
@@ -194,13 +195,13 @@ export const FileUpload = React.forwardRef(function FileUpload({ label, descript
|
|
|
194
195
|
for (const file of incoming) {
|
|
195
196
|
let rejection;
|
|
196
197
|
if (!accepts(file, accept))
|
|
197
|
-
rejection =
|
|
198
|
+
rejection = 'type';
|
|
198
199
|
else if (file.size > maxFileSize)
|
|
199
|
-
rejection =
|
|
200
|
+
rejection = 'size';
|
|
200
201
|
else if (next.some((current) => signature(current) === signature(file)))
|
|
201
|
-
rejection =
|
|
202
|
+
rejection = 'duplicate';
|
|
202
203
|
else if (next.length >= limit)
|
|
203
|
-
rejection =
|
|
204
|
+
rejection = 'count';
|
|
204
205
|
if (rejection)
|
|
205
206
|
rejected.push({ file, reason: rejection });
|
|
206
207
|
else
|
|
@@ -228,14 +229,14 @@ export const FileUpload = React.forwardRef(function FileUpload({ label, descript
|
|
|
228
229
|
requests.current.get(file) === request &&
|
|
229
230
|
!request.controller.signal.aborted &&
|
|
230
231
|
latestFiles.current.includes(file);
|
|
231
|
-
update(file, { status:
|
|
232
|
+
update(file, { status: 'uploading', progress: null });
|
|
232
233
|
try {
|
|
233
234
|
await transport(file, {
|
|
234
235
|
signal: request.controller.signal,
|
|
235
236
|
onProgress(percent) {
|
|
236
237
|
if (current())
|
|
237
238
|
update(file, {
|
|
238
|
-
status:
|
|
239
|
+
status: 'uploading',
|
|
239
240
|
progress: percent === null || !Number.isFinite(percent)
|
|
240
241
|
? null
|
|
241
242
|
: Math.max(0, Math.min(100, percent)),
|
|
@@ -246,18 +247,18 @@ export const FileUpload = React.forwardRef(function FileUpload({ label, descript
|
|
|
246
247
|
if (primaryButtons.current.get(file) ===
|
|
247
248
|
input.current?.ownerDocument.activeElement)
|
|
248
249
|
focusRemove.current = file;
|
|
249
|
-
update(file, { status:
|
|
250
|
+
update(file, { status: 'success', progress: 100 });
|
|
250
251
|
announce(labels.complete(file));
|
|
251
252
|
}
|
|
252
253
|
}
|
|
253
254
|
catch (error) {
|
|
254
255
|
if (current()) {
|
|
255
|
-
let message =
|
|
256
|
+
let message = 'Upload failed. Try again.';
|
|
256
257
|
try {
|
|
257
258
|
message = getUploadError?.(error) || message;
|
|
258
259
|
}
|
|
259
260
|
catch { }
|
|
260
|
-
update(file, { status:
|
|
261
|
+
update(file, { status: 'error', progress: null, error: message });
|
|
261
262
|
announce(labels.failed(file));
|
|
262
263
|
}
|
|
263
264
|
}
|
|
@@ -266,8 +267,8 @@ export const FileUpload = React.forwardRef(function FileUpload({ label, descript
|
|
|
266
267
|
requests.current.delete(file);
|
|
267
268
|
}
|
|
268
269
|
}
|
|
269
|
-
const hasFiles = (event) => Array.from(event.dataTransfer.types).includes(
|
|
270
|
-
return (_jsxs(Field.Root, { ...props, ref: ref, name: name, disabled: disabled, className: skin(
|
|
270
|
+
const hasFiles = (event) => Array.from(event.dataTransfer.types).includes('Files');
|
|
271
|
+
return (_jsxs(Field.Root, { ...props, ref: ref, name: name, disabled: disabled, className: skin('pui-field pui-file-upload', className), "data-dragging": dragging || undefined, children: [_jsx(Field.Label, { children: label }), description != null && description !== false && (_jsx(Field.Description, { children: description })), _jsxs("div", { className: 'pui-file-dropzone', onDragEnter: (event) => {
|
|
271
272
|
onDragEnter?.(event);
|
|
272
273
|
if (!event.defaultPrevented && hasFiles(event) && !disabled) {
|
|
273
274
|
event.preventDefault();
|
|
@@ -283,7 +284,7 @@ export const FileUpload = React.forwardRef(function FileUpload({ label, descript
|
|
|
283
284
|
onDragOver?.(event);
|
|
284
285
|
if (hasFiles(event)) {
|
|
285
286
|
event.preventDefault();
|
|
286
|
-
event.dataTransfer.dropEffect = disabled ?
|
|
287
|
+
event.dataTransfer.dropEffect = disabled ? 'none' : 'copy';
|
|
287
288
|
}
|
|
288
289
|
}, onDrop: (event) => {
|
|
289
290
|
onDrop?.(event);
|
|
@@ -291,53 +292,53 @@ export const FileUpload = React.forwardRef(function FileUpload({ label, descript
|
|
|
291
292
|
setDragging(false);
|
|
292
293
|
if (!event.defaultPrevented && hasFiles(event)) {
|
|
293
294
|
event.preventDefault();
|
|
294
|
-
select(Array.from(event.dataTransfer.files),
|
|
295
|
+
select(Array.from(event.dataTransfer.files), 'drop');
|
|
295
296
|
}
|
|
296
|
-
}, children: [_jsx(Field.Control, { ref: input, id: inputId, name: name, form: form, type:
|
|
297
|
-
if (event.key ===
|
|
297
|
+
}, children: [_jsx(Field.Control, { ref: input, id: inputId, name: name, form: form, type: 'file', accept: accept, capture: capture, multiple: multiple, required: required, disabled: disabled, className: 'pui-file-native', "aria-describedby": errorId, onKeyDown: (event) => {
|
|
298
|
+
if (event.key === 'Enter')
|
|
298
299
|
event.preventBaseUIHandler();
|
|
299
300
|
}, onChange: (event) => {
|
|
300
301
|
if (!notifying.current) {
|
|
301
302
|
event.preventBaseUIHandler();
|
|
302
|
-
select(Array.from(event.currentTarget.files ?? []),
|
|
303
|
+
select(Array.from(event.currentTarget.files ?? []), 'select');
|
|
303
304
|
}
|
|
304
|
-
} }), _jsxs("label", { htmlFor: inputId, className:
|
|
305
|
+
} }), _jsxs("label", { htmlFor: inputId, className: 'pui-button pui-file-trigger', "data-variant": 'outline', "data-disabled": disabled || undefined, children: [_jsx("svg", { width: '16', height: '16', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '1.6', "aria-hidden": 'true', children: _jsx("path", { d: 'M12 16V3m-5 5 5-5 5 5M4 16v5h16v-5' }) }), labels.choose] }), _jsx("p", { className: 'pui-file-drop-hint', children: labels.drop })] }), _jsx("div", { id: errorId, role: 'alert', className: 'pui-file-errors', children: rejections.length > 0 && (_jsx("ul", { children: rejections.map((rejection, index) => (_jsx("li", { children: labels.rejection(rejection) }, index))) })) }), _jsx(Field.Error, {}), files.length > 0 && (_jsx("ul", { className: 'pui-file-list', role: 'list', children: files.map((file) => {
|
|
305
306
|
const state = metadata.get(file) ?? {
|
|
306
|
-
status:
|
|
307
|
+
status: 'ready',
|
|
307
308
|
progress: null,
|
|
308
309
|
};
|
|
309
310
|
if (!identities.current.has(file))
|
|
310
311
|
identities.current.set(file, `${uid}-${++nextId.current}`);
|
|
311
312
|
const itemId = identities.current.get(file);
|
|
312
|
-
return (_jsxs("li", { className:
|
|
313
|
-
?
|
|
314
|
-
: state.status ===
|
|
315
|
-
?
|
|
316
|
-
: state.status ===
|
|
317
|
-
?
|
|
318
|
-
:
|
|
313
|
+
return (_jsxs("li", { className: 'pui-file-item', children: [_jsxs("div", { className: 'pui-file-info', children: [_jsx("strong", { children: file.name }), _jsx("span", { children: sizeLabel(file.size) }), _jsx(StatusBadge, { tone: state.status === 'success'
|
|
314
|
+
? 'success'
|
|
315
|
+
: state.status === 'error'
|
|
316
|
+
? 'danger'
|
|
317
|
+
: state.status === 'uploading'
|
|
318
|
+
? 'progress'
|
|
319
|
+
: 'neutral', children: labels.status[state.status] })] }), state.status === 'uploading' && (_jsx(Progress.Root, { value: state.progress, "aria-label": labels.progress(file), children: _jsx(Progress.Track, { children: _jsx(Progress.Indicator, {}) }) })), state.error && (_jsx("p", { id: `${itemId}-error`, className: 'pui-file-upload-error', children: state.error })), _jsxs("div", { className: 'pui-file-actions', children: [transport && state.status !== 'success' && (_jsx(Button, { ref: (node) => {
|
|
319
320
|
if (node)
|
|
320
321
|
primaryButtons.current.set(file, node);
|
|
321
322
|
else
|
|
322
323
|
primaryButtons.current.delete(file);
|
|
323
|
-
}, type:
|
|
324
|
-
if (state.status ===
|
|
324
|
+
}, type: 'button', size: 'sm', variant: 'outline', disabled: disabled, "aria-describedby": state.error ? `${itemId}-error` : undefined, "aria-label": `${state.status === 'uploading' ? labels.cancel : state.status === 'ready' ? labels.upload : labels.retry} ${file.name}`, onClick: () => {
|
|
325
|
+
if (state.status === 'uploading')
|
|
325
326
|
cancel(file);
|
|
326
327
|
else
|
|
327
328
|
void upload(file);
|
|
328
|
-
}, children: state.status ===
|
|
329
|
+
}, children: state.status === 'uploading'
|
|
329
330
|
? labels.cancel
|
|
330
|
-
: state.status ===
|
|
331
|
+
: state.status === 'ready'
|
|
331
332
|
? labels.upload
|
|
332
|
-
: labels.retry })), _jsx(Button, { type:
|
|
333
|
+
: labels.retry })), _jsx(Button, { type: 'button', size: 'sm', variant: 'ghost', ref: (node) => {
|
|
333
334
|
if (node)
|
|
334
335
|
removeButtons.current.set(file, node);
|
|
335
336
|
else
|
|
336
337
|
removeButtons.current.delete(file);
|
|
337
338
|
}, disabled: disabled, "aria-label": `${labels.remove} ${file.name}`, onClick: () => {
|
|
338
339
|
setRejections([]);
|
|
339
|
-
if (commit(files.filter((current) => current !== file),
|
|
340
|
+
if (commit(files.filter((current) => current !== file), 'remove'))
|
|
340
341
|
focusPicker.current = file;
|
|
341
342
|
}, children: labels.remove })] })] }, itemId));
|
|
342
|
-
}) })), _jsx("span", { className:
|
|
343
|
+
}) })), _jsx("span", { className: 'pui-visually-hidden', role: 'status', children: announcement })] }));
|
|
343
344
|
});
|
package/dist/filter-bar.d.ts
CHANGED
package/dist/filter-bar.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import * as React from
|
|
4
|
-
const cx = (base, extra) => [base, extra].filter(Boolean).join(
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
const cx = (base, extra) => [base, extra].filter(Boolean).join(' ');
|
|
5
5
|
/** Layout only: filtering, routing and async results remain controlled by the caller. */
|
|
6
6
|
const Root = React.forwardRef(function FilterBarRoot({ label, className, ...props }, ref) {
|
|
7
|
-
return (_jsx("form", { ...props, ref: ref, role:
|
|
7
|
+
return (_jsx("form", { ...props, ref: ref, role: 'search', "aria-label": label, className: cx('pui-filter-bar', className) }));
|
|
8
8
|
});
|
|
9
9
|
const Controls = React.forwardRef(function FilterBarControls({ className, ...props }, ref) {
|
|
10
|
-
return (_jsx("div", { ...props, ref: ref, className: cx(
|
|
10
|
+
return (_jsx("div", { ...props, ref: ref, className: cx('pui-filter-bar-controls', className) }));
|
|
11
11
|
});
|
|
12
|
-
const Field = React.forwardRef(function FilterBarField({ width =
|
|
13
|
-
return (_jsx("label", { ...props, ref: ref, "data-width": width, className: cx(
|
|
12
|
+
const Field = React.forwardRef(function FilterBarField({ width = 'filter', className, ...props }, ref) {
|
|
13
|
+
return (_jsx("label", { ...props, ref: ref, "data-width": width, className: cx('pui-filter-bar-field', className) }));
|
|
14
14
|
});
|
|
15
15
|
const Actions = React.forwardRef(function FilterBarActions({ className, ...props }, ref) {
|
|
16
|
-
return (_jsx("div", { ...props, ref: ref, className: cx(
|
|
16
|
+
return (_jsx("div", { ...props, ref: ref, className: cx('pui-filter-bar-actions', className) }));
|
|
17
17
|
});
|
|
18
18
|
const Summary = React.forwardRef(function FilterBarSummary({ className, ...props }, ref) {
|
|
19
|
-
return (_jsx("div", { role:
|
|
19
|
+
return (_jsx("div", { role: 'status', "aria-atomic": 'true', ...props, ref: ref, className: cx('pui-filter-bar-summary', className) }));
|
|
20
20
|
});
|
|
21
21
|
export const FilterBar = { Root, Controls, Field, Actions, Summary };
|
|
22
22
|
// Flat names for React Server Components, which cannot dot into client exports.
|
package/dist/form-state.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as React from
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
/** Share the actual DOM node, including React 19 callback-ref cleanup semantics. */
|
|
3
3
|
export declare function useRootRef<Element extends HTMLElement>(forwarded: React.Ref<Element> | undefined): readonly [React.RefObject<Element | null>, (node: Element | null) => () => void];
|
|
4
4
|
/** Reset stateful primitives with their native form, without firing change callbacks. */
|
package/dist/form-state.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
import * as React from
|
|
1
|
+
'use client';
|
|
2
|
+
import * as React from 'react';
|
|
3
3
|
/** Share the actual DOM node, including React 19 callback-ref cleanup semantics. */
|
|
4
4
|
export function useRootRef(forwarded) {
|
|
5
5
|
const element = React.useRef(null);
|
|
6
6
|
const attach = React.useCallback((node) => {
|
|
7
7
|
element.current = node;
|
|
8
|
-
const cleanup = typeof forwarded ===
|
|
9
|
-
if (forwarded && typeof forwarded !==
|
|
8
|
+
const cleanup = typeof forwarded === 'function' ? forwarded(node) : undefined;
|
|
9
|
+
if (forwarded && typeof forwarded !== 'function')
|
|
10
10
|
forwarded.current = node;
|
|
11
11
|
return () => {
|
|
12
12
|
if (element.current === node)
|
|
13
13
|
element.current = null;
|
|
14
|
-
if (typeof cleanup ===
|
|
14
|
+
if (typeof cleanup === 'function')
|
|
15
15
|
cleanup();
|
|
16
|
-
else if (typeof forwarded ===
|
|
16
|
+
else if (typeof forwarded === 'function')
|
|
17
17
|
forwarded(null);
|
|
18
18
|
else if (forwarded)
|
|
19
19
|
forwarded.current = null;
|
|
@@ -30,9 +30,9 @@ export function useResetValue(controlled, initial, element, formId, afterReset)
|
|
|
30
30
|
React.useEffect(() => {
|
|
31
31
|
const node = element.current;
|
|
32
32
|
const owner = (formId ? node?.ownerDocument.getElementById(formId) : undefined) ??
|
|
33
|
-
node?.closest(
|
|
34
|
-
Array.from(node?.querySelectorAll(
|
|
35
|
-
if (!owner || owner.tagName !==
|
|
33
|
+
node?.closest('form') ??
|
|
34
|
+
Array.from(node?.querySelectorAll('input') ?? []).find((input) => input.form)?.form;
|
|
35
|
+
if (!owner || owner.tagName !== 'FORM')
|
|
36
36
|
return;
|
|
37
37
|
const pending = new Set();
|
|
38
38
|
const reset = (event) => {
|
|
@@ -49,9 +49,9 @@ export function useResetValue(controlled, initial, element, formId, afterReset)
|
|
|
49
49
|
}, 0);
|
|
50
50
|
pending.add(timer);
|
|
51
51
|
};
|
|
52
|
-
owner.addEventListener(
|
|
52
|
+
owner.addEventListener('reset', reset);
|
|
53
53
|
return () => {
|
|
54
|
-
owner.removeEventListener(
|
|
54
|
+
owner.removeEventListener('reset', reset);
|
|
55
55
|
pending.forEach(clearTimeout);
|
|
56
56
|
};
|
|
57
57
|
}, [element, formId]);
|
package/dist/forms.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import { type ComponentSize } from
|
|
3
|
-
import { Form as BaseForm } from
|
|
4
|
-
import { Radio as BaseRadio } from
|
|
5
|
-
import { RadioGroup as BaseRadioGroup } from
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type ComponentSize } from './sizing.js';
|
|
3
|
+
import { Form as BaseForm } from '@base-ui/react/form';
|
|
4
|
+
import { Radio as BaseRadio } from '@base-ui/react/radio';
|
|
5
|
+
import { RadioGroup as BaseRadioGroup } from '@base-ui/react/radio-group';
|
|
6
6
|
declare const FieldRoot: React.ForwardRefExoticComponent<Omit<Omit<Omit<import("@base-ui/react/field").FieldRootProps, "ref"> & React.RefAttributes<HTMLDivElement>, "ref">, "size"> & {
|
|
7
7
|
size?: ComponentSize;
|
|
8
8
|
} & React.RefAttributes<HTMLDivElement>>;
|