@a-type/ui 6.1.33 → 6.1.34
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/imageUploader/ImageUploader.d.ts +10 -1
- package/dist/components/imageUploader/ImageUploader.js +48 -4
- package/dist/components/imageUploader/ImageUploader.js.map +1 -1
- package/dist/components/imageUploader/ImageUploader.module.css +34 -0
- package/dist/components/imageUploader/ImageUploader.stories.d.ts +6 -0
- package/dist/components/imageUploader/ImageUploader.stories.js +16 -0
- package/dist/components/imageUploader/ImageUploader.stories.js.map +1 -1
- package/package.json +1 -1
- package/src/components/imageUploader/ImageUploader.module.css +34 -0
- package/src/components/imageUploader/ImageUploader.stories.tsx +41 -0
- package/src/components/imageUploader/ImageUploader.tsx +118 -1
|
@@ -3,6 +3,8 @@ import { ButtonProps } from '../button/index.js';
|
|
|
3
3
|
export interface ImageUploaderProps {
|
|
4
4
|
value: string | null;
|
|
5
5
|
onChange: (value: File | null) => void;
|
|
6
|
+
altText?: string;
|
|
7
|
+
onAltText?: (value: string) => void;
|
|
6
8
|
className?: string;
|
|
7
9
|
style?: CSSProperties;
|
|
8
10
|
maxDimension?: number;
|
|
@@ -13,7 +15,7 @@ export interface ImageUploaderProps {
|
|
|
13
15
|
* on the image to select a new one, or dragging a new image onto the
|
|
14
16
|
* component to replace the existing one.
|
|
15
17
|
*/
|
|
16
|
-
export declare function ImageUploaderRoot({ value, onChange: handleChange, maxDimension, children, className, ...rest }: ImageUploaderProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export declare function ImageUploaderRoot({ value, onChange: handleChange, altText, onAltText, maxDimension, children, className, ...rest }: ImageUploaderProps): import("react/jsx-runtime").JSX.Element;
|
|
17
19
|
declare function ImageUploaderPrebuilt({ crossOrigin, ...props }: Omit<ImageUploaderProps, 'children'> & {
|
|
18
20
|
facingMode?: 'user' | 'environment';
|
|
19
21
|
crossOrigin?: ImgHTMLAttributes<HTMLImageElement>['crossOrigin'];
|
|
@@ -21,6 +23,12 @@ declare function ImageUploaderPrebuilt({ crossOrigin, ...props }: Omit<ImageUplo
|
|
|
21
23
|
export declare function ImageUploaderFileButton({ children, ...props }: ButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
22
24
|
export declare function ImageUploaderRemoveButton({ className, ...rest }: ButtonProps): import("react/jsx-runtime").JSX.Element | null;
|
|
23
25
|
export declare function ImageUploaderDisplay({ className, ...rest }: ImgHTMLAttributes<HTMLImageElement>): import("react/jsx-runtime").JSX.Element | null;
|
|
26
|
+
export interface ImageUploaderAltTextProps {
|
|
27
|
+
className?: string;
|
|
28
|
+
altText?: string;
|
|
29
|
+
onAltText?: (value: string) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare function ImageUploaderAltText({ className, altText, onAltText, }: ImageUploaderAltTextProps): import("react/jsx-runtime").JSX.Element | null;
|
|
24
32
|
export declare function ImageUploaderEmptyControls({ children, className, }: {
|
|
25
33
|
children: ReactNode;
|
|
26
34
|
className?: string;
|
|
@@ -30,6 +38,7 @@ export declare const ImageUploader: typeof ImageUploaderPrebuilt & {
|
|
|
30
38
|
FileButton: typeof ImageUploaderFileButton;
|
|
31
39
|
RemoveButton: typeof ImageUploaderRemoveButton;
|
|
32
40
|
Display: typeof ImageUploaderDisplay;
|
|
41
|
+
AltText: typeof ImageUploaderAltText;
|
|
33
42
|
EmptyControls: typeof ImageUploaderEmptyControls;
|
|
34
43
|
};
|
|
35
44
|
export {};
|
|
@@ -11,10 +11,13 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
import classNames, { clsx } from 'clsx';
|
|
14
|
-
import { createContext, useCallback, useContext, useEffect, useId, useState, } from 'react';
|
|
14
|
+
import { createContext, useCallback, useContext, useEffect, useId, useRef, useState, } from 'react';
|
|
15
15
|
import { Button } from '../button/index.js';
|
|
16
16
|
import { CameraDeviceSelector, CameraFullscreenButton, CameraRoot, CameraShutterButton, } from '../camera/index.js';
|
|
17
|
+
import { Dialog } from '../dialog/index.js';
|
|
18
|
+
import { Field } from '../forms/index.js';
|
|
17
19
|
import { Icon } from '../icon/index.js';
|
|
20
|
+
import { TextArea } from '../textArea/index.js';
|
|
18
21
|
import cls from './ImageUploader.module.css';
|
|
19
22
|
/**
|
|
20
23
|
* Renders an image if one is already set, and allows either clicking
|
|
@@ -22,7 +25,7 @@ import cls from './ImageUploader.module.css';
|
|
|
22
25
|
* component to replace the existing one.
|
|
23
26
|
*/
|
|
24
27
|
export function ImageUploaderRoot(_a) {
|
|
25
|
-
var { value, onChange: handleChange, maxDimension, children, className } = _a, rest = __rest(_a, ["value", "onChange", "maxDimension", "children", "className"]);
|
|
28
|
+
var { value, onChange: handleChange, altText, onAltText, maxDimension, children, className } = _a, rest = __rest(_a, ["value", "onChange", "altText", "onAltText", "maxDimension", "children", "className"]);
|
|
26
29
|
const inputId = useId();
|
|
27
30
|
const [dragging, setDragging] = useState(false);
|
|
28
31
|
const [draggingOver, setDraggingOver] = useState(false);
|
|
@@ -93,7 +96,15 @@ export function ImageUploaderRoot(_a) {
|
|
|
93
96
|
const onFileClick = useCallback((e) => {
|
|
94
97
|
e.stopPropagation();
|
|
95
98
|
}, []);
|
|
96
|
-
return (_jsx(ImageUploaderContext.Provider, { value: {
|
|
99
|
+
return (_jsx(ImageUploaderContext.Provider, { value: {
|
|
100
|
+
inputId,
|
|
101
|
+
dragging,
|
|
102
|
+
draggingOver,
|
|
103
|
+
value,
|
|
104
|
+
onChange,
|
|
105
|
+
altText,
|
|
106
|
+
onAltText,
|
|
107
|
+
}, children: _jsxs("div", Object.assign({ className: classNames('@mode-neutral', cls.root, className), onDragEnter: onDragEnter, onDragLeave: onDragLeave, onDragOver: onDragOver, onDrop: onDrop, onDragStart: onDragStart, onDragEnd: onDragEnd }, rest, { children: [_jsx("input", { type: "file", accept: "image/*", onChange: onFileChange, onClick: onFileClick, className: cls.input, id: inputId }), children] })) }));
|
|
97
108
|
}
|
|
98
109
|
const ImageUploaderContext = createContext(null);
|
|
99
110
|
function useUploaderContext() {
|
|
@@ -107,7 +118,7 @@ function ImageUploaderPrebuilt(_a) {
|
|
|
107
118
|
var { crossOrigin = 'anonymous' } = _a, props = __rest(_a, ["crossOrigin"]);
|
|
108
119
|
const [cameraOpen, setCameraOpen] = useState(false);
|
|
109
120
|
const openCamera = () => setCameraOpen(true);
|
|
110
|
-
return (_jsxs(ImageUploaderRoot, Object.assign({}, props, { children: [_jsx(ImageUploaderDisplay, { crossOrigin: crossOrigin }), _jsxs(ImageUploaderEmptyControls, { children: [_jsx(ImageUploaderFileButton, {}), _jsxs(Button, { emphasis: "ghost", size: "small", onClick: openCamera, children: [_jsx(Icon, { name: "camera" }), _jsx("span", { children: "Camera" })] })] }), !props.value && cameraOpen && (_jsxs(CameraRoot, { className: cls.cameraRoot, format: "image/png", onCapture: (file) => {
|
|
121
|
+
return (_jsxs(ImageUploaderRoot, Object.assign({}, props, { children: [_jsx(ImageUploaderDisplay, { crossOrigin: crossOrigin }), (props.altText !== undefined || props.onAltText) && (_jsx(ImageUploaderAltText, {})), _jsxs(ImageUploaderEmptyControls, { children: [_jsx(ImageUploaderFileButton, {}), _jsxs(Button, { emphasis: "ghost", size: "small", onClick: openCamera, children: [_jsx(Icon, { name: "camera" }), _jsx("span", { children: "Camera" })] })] }), !props.value && cameraOpen && (_jsxs(CameraRoot, { className: cls.cameraRoot, format: "image/png", onCapture: (file) => {
|
|
111
122
|
props.onChange(file);
|
|
112
123
|
setCameraOpen(false);
|
|
113
124
|
}, facingMode: props.facingMode, children: [_jsx(CameraShutterButton, {}), _jsx(CameraDeviceSelector, {}), _jsx(CameraFullscreenButton, {}), _jsx(Button, { onClick: () => setCameraOpen(false), emphasis: "ghost", size: "small", className: cls.cameraCancelButton, children: "Cancel" })] })), _jsx(ImageUploaderRemoveButton, {})] })));
|
|
@@ -149,6 +160,38 @@ export function ImageUploaderDisplay(_a) {
|
|
|
149
160
|
const { value } = useUploaderContext();
|
|
150
161
|
return value ? (_jsx("img", Object.assign({ src: value, className: clsx(cls.imagePreview, className) }, rest))) : null;
|
|
151
162
|
}
|
|
163
|
+
export function ImageUploaderAltText({ className, altText, onAltText, }) {
|
|
164
|
+
const { value, altText: contextAltText, onAltText: contextOnAltText, } = useUploaderContext();
|
|
165
|
+
const finalAltText = altText !== null && altText !== void 0 ? altText : contextAltText;
|
|
166
|
+
const finalOnAltText = onAltText !== null && onAltText !== void 0 ? onAltText : contextOnAltText;
|
|
167
|
+
const enabled = finalAltText !== undefined || !!finalOnAltText;
|
|
168
|
+
const [open, setOpen] = useState(false);
|
|
169
|
+
const [innerAltText, setInnerAltText] = useState('');
|
|
170
|
+
const skipCloseResetRef = useRef(false);
|
|
171
|
+
const readOnly = !finalOnAltText;
|
|
172
|
+
const altTextFieldId = useId();
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (open)
|
|
175
|
+
return;
|
|
176
|
+
setInnerAltText(finalAltText !== null && finalAltText !== void 0 ? finalAltText : '');
|
|
177
|
+
}, [finalAltText, open]);
|
|
178
|
+
if (!enabled || !value)
|
|
179
|
+
return null;
|
|
180
|
+
return (_jsxs(Dialog, { open: open, onOpenChange: (isOpen) => {
|
|
181
|
+
setOpen(isOpen);
|
|
182
|
+
if (!isOpen) {
|
|
183
|
+
if (skipCloseResetRef.current) {
|
|
184
|
+
skipCloseResetRef.current = false;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
setInnerAltText(finalAltText !== null && finalAltText !== void 0 ? finalAltText : '');
|
|
188
|
+
}
|
|
189
|
+
}, disableSheet: true, children: [_jsx(Dialog.Trigger, { className: clsx(cls.altTextButton, className), render: _jsx(Button, { emphasis: "light", size: "small" }), "aria-label": readOnly ? 'View alt text' : 'Edit alt text', children: "ALT" }), _jsxs(Dialog.Content, { className: cls.altTextDialog, innerClassName: cls.altTextDialogInner, children: [_jsx(Dialog.Title, { children: "Alt text" }), _jsx("img", { src: value, alt: "", className: cls.altTextDialogImage }), _jsxs(Field, { stretch: true, id: altTextFieldId, className: cls.altTextField, children: [_jsx(Field.Label, { children: "Alt text" }), _jsx(Field.Control, { render: _jsx(TextArea, { autoSize: false, rows: 4, value: innerAltText, readOnly: readOnly, onValueChange: readOnly ? undefined : setInnerAltText, className: cls.altTextInput }) })] }), finalOnAltText && (_jsxs(Dialog.Actions, { children: [_jsx(Dialog.Close, { children: "Cancel" }), _jsx(Button, { emphasis: "primary", onClick: () => {
|
|
190
|
+
skipCloseResetRef.current = true;
|
|
191
|
+
finalOnAltText(innerAltText);
|
|
192
|
+
setOpen(false);
|
|
193
|
+
}, children: "Save" })] }))] })] }));
|
|
194
|
+
}
|
|
152
195
|
export function ImageUploaderEmptyControls({ children, className, }) {
|
|
153
196
|
const { value, draggingOver } = useUploaderContext();
|
|
154
197
|
if (value)
|
|
@@ -160,6 +203,7 @@ export const ImageUploader = Object.assign(ImageUploaderPrebuilt, {
|
|
|
160
203
|
FileButton: ImageUploaderFileButton,
|
|
161
204
|
RemoveButton: ImageUploaderRemoveButton,
|
|
162
205
|
Display: ImageUploaderDisplay,
|
|
206
|
+
AltText: ImageUploaderAltText,
|
|
163
207
|
EmptyControls: ImageUploaderEmptyControls,
|
|
164
208
|
});
|
|
165
209
|
//# sourceMappingURL=ImageUploader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageUploader.js","sourceRoot":"","sources":["../../../src/components/imageUploader/ImageUploader.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EACN,aAAa,EAIb,WAAW,EACX,UAAU,EACV,SAAS,EACT,KAAK,EACL,QAAQ,GACR,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,MAAM,EAAe,MAAM,oBAAoB,CAAC;AACzD,OAAO,EACN,oBAAoB,EACpB,sBAAsB,EACtB,UAAU,EACV,mBAAmB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,GAAG,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"ImageUploader.js","sourceRoot":"","sources":["../../../src/components/imageUploader/ImageUploader.tsx"],"names":[],"mappings":"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"}
|
|
@@ -40,6 +40,40 @@
|
|
|
40
40
|
border: none;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
.altTextButton {
|
|
44
|
+
position: absolute;
|
|
45
|
+
right: var(--m-sp-sm);
|
|
46
|
+
bottom: var(--m-sp-sm);
|
|
47
|
+
z-index: 10;
|
|
48
|
+
font-weight: var(--m-fw-bold);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.altTextDialog {
|
|
52
|
+
max-width: min(560px, 90vw);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.altTextDialogInner {
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
gap: var(--m-sp-md);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.altTextField {
|
|
62
|
+
width: 100%;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.altTextInput {
|
|
66
|
+
width: 100%;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.altTextDialogImage {
|
|
70
|
+
width: 100%;
|
|
71
|
+
max-height: 280px;
|
|
72
|
+
object-fit: contain;
|
|
73
|
+
border-radius: var(--m-rd-md);
|
|
74
|
+
@apply --mx-bg(var(--m-surface-ambient-bg));
|
|
75
|
+
}
|
|
76
|
+
|
|
43
77
|
.imagePreview {
|
|
44
78
|
width: 100%;
|
|
45
79
|
height: 100%;
|
|
@@ -10,12 +10,16 @@ declare const meta: {
|
|
|
10
10
|
FileButton: typeof import("./ImageUploader.js").ImageUploaderFileButton;
|
|
11
11
|
RemoveButton: typeof import("./ImageUploader.js").ImageUploaderRemoveButton;
|
|
12
12
|
Display: typeof import("./ImageUploader.js").ImageUploaderDisplay;
|
|
13
|
+
AltText: typeof import("./ImageUploader.js").ImageUploaderAltText;
|
|
13
14
|
EmptyControls: typeof import("./ImageUploader.js").ImageUploaderEmptyControls;
|
|
14
15
|
};
|
|
15
16
|
argTypes: {
|
|
16
17
|
onChange: {
|
|
17
18
|
action: string;
|
|
18
19
|
};
|
|
20
|
+
onAltText: {
|
|
21
|
+
action: string;
|
|
22
|
+
};
|
|
19
23
|
maxDimension: {
|
|
20
24
|
control: {
|
|
21
25
|
type: "number";
|
|
@@ -37,3 +41,5 @@ declare const meta: {
|
|
|
37
41
|
export default meta;
|
|
38
42
|
type Story = StoryObj<typeof ImageUploader>;
|
|
39
43
|
export declare const Default: Story;
|
|
44
|
+
export declare const WithAltText: Story;
|
|
45
|
+
export declare const ReadOnlyAltText: Story;
|
|
@@ -6,6 +6,7 @@ const meta = {
|
|
|
6
6
|
component: ImageUploader,
|
|
7
7
|
argTypes: {
|
|
8
8
|
onChange: { action: 'change' },
|
|
9
|
+
onAltText: { action: 'alt text' },
|
|
9
10
|
maxDimension: { control: { type: 'number' } },
|
|
10
11
|
},
|
|
11
12
|
parameters: {
|
|
@@ -19,8 +20,23 @@ export default meta;
|
|
|
19
20
|
export const Default = {
|
|
20
21
|
render: () => _jsx(ImageUploaderDemo, {}),
|
|
21
22
|
};
|
|
23
|
+
export const WithAltText = {
|
|
24
|
+
render: () => _jsx(ImageUploaderAltTextDemo, {}),
|
|
25
|
+
};
|
|
26
|
+
export const ReadOnlyAltText = {
|
|
27
|
+
render: () => _jsx(ImageUploaderReadOnlyAltTextDemo, {}),
|
|
28
|
+
};
|
|
22
29
|
function ImageUploaderDemo() {
|
|
23
30
|
const [value, setValue] = useState(null);
|
|
24
31
|
return (_jsx(ImageUploader, { onChange: (file) => file ? setValue(URL.createObjectURL(file)) : setValue(null), value: value, style: { width: 256, height: 256 } }));
|
|
25
32
|
}
|
|
33
|
+
function ImageUploaderAltTextDemo() {
|
|
34
|
+
const [value, setValue] = useState(null);
|
|
35
|
+
const [altText, setAltText] = useState('A scenic mountain landscape');
|
|
36
|
+
return (_jsx(ImageUploader, { onChange: (file) => file ? setValue(URL.createObjectURL(file)) : setValue(null), onAltText: setAltText, altText: altText, value: value, style: { width: 256, height: 256 } }));
|
|
37
|
+
}
|
|
38
|
+
function ImageUploaderReadOnlyAltTextDemo() {
|
|
39
|
+
const [value, setValue] = useState(null);
|
|
40
|
+
return (_jsx(ImageUploader, { onChange: (file) => file ? setValue(URL.createObjectURL(file)) : setValue(null), altText: "Read-only alt text", value: value, style: { width: 256, height: 256 } }));
|
|
41
|
+
}
|
|
26
42
|
//# sourceMappingURL=ImageUploader.stories.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageUploader.stories.js","sourceRoot":"","sources":["../../../src/components/imageUploader/ImageUploader.stories.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,IAAI,GAAG;IACZ,KAAK,EAAE,0BAA0B;IACjC,SAAS,EAAE,aAAa;IACxB,QAAQ,EAAE;QACT,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;QAC9B,YAAY,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KAC7C;IACD,UAAU,EAAE;QACX,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC5B;IACD,IAAI,EAAE;QACL,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;KAClC;CACoC,CAAC;AAEvC,eAAe,IAAI,CAAC;AAIpB,MAAM,CAAC,MAAM,OAAO,GAAU;IAC7B,MAAM,EAAE,GAAG,EAAE,CAAC,KAAC,iBAAiB,KAAG;CACnC,CAAC;AAEF,SAAS,iBAAiB;IACzB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAExD,OAAO,CACN,KAAC,aAAa,IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAClB,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAE5D,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GACjC,CACF,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"ImageUploader.stories.js","sourceRoot":"","sources":["../../../src/components/imageUploader/ImageUploader.stories.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,IAAI,GAAG;IACZ,KAAK,EAAE,0BAA0B;IACjC,SAAS,EAAE,aAAa;IACxB,QAAQ,EAAE;QACT,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;QAC9B,SAAS,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE;QACjC,YAAY,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KAC7C;IACD,UAAU,EAAE;QACX,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC5B;IACD,IAAI,EAAE;QACL,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;KAClC;CACoC,CAAC;AAEvC,eAAe,IAAI,CAAC;AAIpB,MAAM,CAAC,MAAM,OAAO,GAAU;IAC7B,MAAM,EAAE,GAAG,EAAE,CAAC,KAAC,iBAAiB,KAAG;CACnC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAU;IACjC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAC,wBAAwB,KAAG;CAC1C,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAU;IACrC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAC,gCAAgC,KAAG;CAClD,CAAC;AAEF,SAAS,iBAAiB;IACzB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAExD,OAAO,CACN,KAAC,aAAa,IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAClB,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAE5D,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GACjC,CACF,CAAC;AACH,CAAC;AAED,SAAS,wBAAwB;IAChC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,6BAA6B,CAAC,CAAC;IAEtE,OAAO,CACN,KAAC,aAAa,IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAClB,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAE5D,SAAS,EAAE,UAAU,EACrB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GACjC,CACF,CAAC;AACH,CAAC;AAED,SAAS,gCAAgC;IACxC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAExD,OAAO,CACN,KAAC,aAAa,IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAClB,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAE5D,OAAO,EAAC,oBAAoB,EAC5B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GACjC,CACF,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -40,6 +40,40 @@
|
|
|
40
40
|
border: none;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
.altTextButton {
|
|
44
|
+
position: absolute;
|
|
45
|
+
right: var(--m-sp-sm);
|
|
46
|
+
bottom: var(--m-sp-sm);
|
|
47
|
+
z-index: 10;
|
|
48
|
+
font-weight: var(--m-fw-bold);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.altTextDialog {
|
|
52
|
+
max-width: min(560px, 90vw);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.altTextDialogInner {
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
gap: var(--m-sp-md);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.altTextField {
|
|
62
|
+
width: 100%;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.altTextInput {
|
|
66
|
+
width: 100%;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.altTextDialogImage {
|
|
70
|
+
width: 100%;
|
|
71
|
+
max-height: 280px;
|
|
72
|
+
object-fit: contain;
|
|
73
|
+
border-radius: var(--m-rd-md);
|
|
74
|
+
@apply --mx-bg(var(--m-surface-ambient-bg));
|
|
75
|
+
}
|
|
76
|
+
|
|
43
77
|
.imagePreview {
|
|
44
78
|
width: 100%;
|
|
45
79
|
height: 100%;
|
|
@@ -7,6 +7,7 @@ const meta = {
|
|
|
7
7
|
component: ImageUploader,
|
|
8
8
|
argTypes: {
|
|
9
9
|
onChange: { action: 'change' },
|
|
10
|
+
onAltText: { action: 'alt text' },
|
|
10
11
|
maxDimension: { control: { type: 'number' } },
|
|
11
12
|
},
|
|
12
13
|
parameters: {
|
|
@@ -25,6 +26,14 @@ export const Default: Story = {
|
|
|
25
26
|
render: () => <ImageUploaderDemo />,
|
|
26
27
|
};
|
|
27
28
|
|
|
29
|
+
export const WithAltText: Story = {
|
|
30
|
+
render: () => <ImageUploaderAltTextDemo />,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const ReadOnlyAltText: Story = {
|
|
34
|
+
render: () => <ImageUploaderReadOnlyAltTextDemo />,
|
|
35
|
+
};
|
|
36
|
+
|
|
28
37
|
function ImageUploaderDemo() {
|
|
29
38
|
const [value, setValue] = useState<string | null>(null);
|
|
30
39
|
|
|
@@ -38,3 +47,35 @@ function ImageUploaderDemo() {
|
|
|
38
47
|
/>
|
|
39
48
|
);
|
|
40
49
|
}
|
|
50
|
+
|
|
51
|
+
function ImageUploaderAltTextDemo() {
|
|
52
|
+
const [value, setValue] = useState<string | null>(null);
|
|
53
|
+
const [altText, setAltText] = useState('A scenic mountain landscape');
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<ImageUploader
|
|
57
|
+
onChange={(file) =>
|
|
58
|
+
file ? setValue(URL.createObjectURL(file)) : setValue(null)
|
|
59
|
+
}
|
|
60
|
+
onAltText={setAltText}
|
|
61
|
+
altText={altText}
|
|
62
|
+
value={value}
|
|
63
|
+
style={{ width: 256, height: 256 }}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function ImageUploaderReadOnlyAltTextDemo() {
|
|
69
|
+
const [value, setValue] = useState<string | null>(null);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<ImageUploader
|
|
73
|
+
onChange={(file) =>
|
|
74
|
+
file ? setValue(URL.createObjectURL(file)) : setValue(null)
|
|
75
|
+
}
|
|
76
|
+
altText="Read-only alt text"
|
|
77
|
+
value={value}
|
|
78
|
+
style={{ width: 256, height: 256 }}
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
useContext,
|
|
9
9
|
useEffect,
|
|
10
10
|
useId,
|
|
11
|
+
useRef,
|
|
11
12
|
useState,
|
|
12
13
|
} from 'react';
|
|
13
14
|
import { Button, ButtonProps } from '../button/index.js';
|
|
@@ -17,12 +18,17 @@ import {
|
|
|
17
18
|
CameraRoot,
|
|
18
19
|
CameraShutterButton,
|
|
19
20
|
} from '../camera/index.js';
|
|
21
|
+
import { Dialog } from '../dialog/index.js';
|
|
22
|
+
import { Field } from '../forms/index.js';
|
|
20
23
|
import { Icon } from '../icon/index.js';
|
|
24
|
+
import { TextArea } from '../textArea/index.js';
|
|
21
25
|
import cls from './ImageUploader.module.css';
|
|
22
26
|
|
|
23
27
|
export interface ImageUploaderProps {
|
|
24
28
|
value: string | null;
|
|
25
29
|
onChange: (value: File | null) => void;
|
|
30
|
+
altText?: string;
|
|
31
|
+
onAltText?: (value: string) => void;
|
|
26
32
|
className?: string;
|
|
27
33
|
style?: CSSProperties;
|
|
28
34
|
maxDimension?: number;
|
|
@@ -37,6 +43,8 @@ export interface ImageUploaderProps {
|
|
|
37
43
|
export function ImageUploaderRoot({
|
|
38
44
|
value,
|
|
39
45
|
onChange: handleChange,
|
|
46
|
+
altText,
|
|
47
|
+
onAltText,
|
|
40
48
|
maxDimension,
|
|
41
49
|
children,
|
|
42
50
|
className,
|
|
@@ -134,7 +142,15 @@ export function ImageUploaderRoot({
|
|
|
134
142
|
|
|
135
143
|
return (
|
|
136
144
|
<ImageUploaderContext.Provider
|
|
137
|
-
value={{
|
|
145
|
+
value={{
|
|
146
|
+
inputId,
|
|
147
|
+
dragging,
|
|
148
|
+
draggingOver,
|
|
149
|
+
value,
|
|
150
|
+
onChange,
|
|
151
|
+
altText,
|
|
152
|
+
onAltText,
|
|
153
|
+
}}
|
|
138
154
|
>
|
|
139
155
|
<div
|
|
140
156
|
className={classNames('@mode-neutral', cls.root, className)}
|
|
@@ -166,6 +182,8 @@ const ImageUploaderContext = createContext<{
|
|
|
166
182
|
draggingOver: boolean;
|
|
167
183
|
value: string | null;
|
|
168
184
|
onChange: (file: File | null) => void;
|
|
185
|
+
altText?: string;
|
|
186
|
+
onAltText?: (value: string) => void;
|
|
169
187
|
} | null>(null);
|
|
170
188
|
function useUploaderContext() {
|
|
171
189
|
const context = useContext(ImageUploaderContext);
|
|
@@ -188,6 +206,9 @@ function ImageUploaderPrebuilt({
|
|
|
188
206
|
return (
|
|
189
207
|
<ImageUploaderRoot {...props}>
|
|
190
208
|
<ImageUploaderDisplay crossOrigin={crossOrigin} />
|
|
209
|
+
{(props.altText !== undefined || props.onAltText) && (
|
|
210
|
+
<ImageUploaderAltText />
|
|
211
|
+
)}
|
|
191
212
|
|
|
192
213
|
<ImageUploaderEmptyControls>
|
|
193
214
|
<ImageUploaderFileButton />
|
|
@@ -291,6 +312,101 @@ export function ImageUploaderDisplay({
|
|
|
291
312
|
) : null;
|
|
292
313
|
}
|
|
293
314
|
|
|
315
|
+
export interface ImageUploaderAltTextProps {
|
|
316
|
+
className?: string;
|
|
317
|
+
altText?: string;
|
|
318
|
+
onAltText?: (value: string) => void;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function ImageUploaderAltText({
|
|
322
|
+
className,
|
|
323
|
+
altText,
|
|
324
|
+
onAltText,
|
|
325
|
+
}: ImageUploaderAltTextProps) {
|
|
326
|
+
const {
|
|
327
|
+
value,
|
|
328
|
+
altText: contextAltText,
|
|
329
|
+
onAltText: contextOnAltText,
|
|
330
|
+
} = useUploaderContext();
|
|
331
|
+
const finalAltText = altText ?? contextAltText;
|
|
332
|
+
const finalOnAltText = onAltText ?? contextOnAltText;
|
|
333
|
+
const enabled = finalAltText !== undefined || !!finalOnAltText;
|
|
334
|
+
const [open, setOpen] = useState(false);
|
|
335
|
+
const [innerAltText, setInnerAltText] = useState('');
|
|
336
|
+
const skipCloseResetRef = useRef(false);
|
|
337
|
+
const readOnly = !finalOnAltText;
|
|
338
|
+
const altTextFieldId = useId();
|
|
339
|
+
|
|
340
|
+
useEffect(() => {
|
|
341
|
+
if (open) return;
|
|
342
|
+
setInnerAltText(finalAltText ?? '');
|
|
343
|
+
}, [finalAltText, open]);
|
|
344
|
+
|
|
345
|
+
if (!enabled || !value) return null;
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
<Dialog
|
|
349
|
+
open={open}
|
|
350
|
+
onOpenChange={(isOpen) => {
|
|
351
|
+
setOpen(isOpen);
|
|
352
|
+
if (!isOpen) {
|
|
353
|
+
if (skipCloseResetRef.current) {
|
|
354
|
+
skipCloseResetRef.current = false;
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
setInnerAltText(finalAltText ?? '');
|
|
358
|
+
}
|
|
359
|
+
}}
|
|
360
|
+
disableSheet
|
|
361
|
+
>
|
|
362
|
+
<Dialog.Trigger
|
|
363
|
+
className={clsx(cls.altTextButton, className)}
|
|
364
|
+
render={<Button emphasis="light" size="small" />}
|
|
365
|
+
aria-label={readOnly ? 'View alt text' : 'Edit alt text'}
|
|
366
|
+
>
|
|
367
|
+
ALT
|
|
368
|
+
</Dialog.Trigger>
|
|
369
|
+
<Dialog.Content
|
|
370
|
+
className={cls.altTextDialog}
|
|
371
|
+
innerClassName={cls.altTextDialogInner}
|
|
372
|
+
>
|
|
373
|
+
<Dialog.Title>Alt text</Dialog.Title>
|
|
374
|
+
<img src={value} alt="" className={cls.altTextDialogImage} />
|
|
375
|
+
<Field stretch id={altTextFieldId} className={cls.altTextField}>
|
|
376
|
+
<Field.Label>Alt text</Field.Label>
|
|
377
|
+
<Field.Control
|
|
378
|
+
render={
|
|
379
|
+
<TextArea
|
|
380
|
+
autoSize={false}
|
|
381
|
+
rows={4}
|
|
382
|
+
value={innerAltText}
|
|
383
|
+
readOnly={readOnly}
|
|
384
|
+
onValueChange={readOnly ? undefined : setInnerAltText}
|
|
385
|
+
className={cls.altTextInput}
|
|
386
|
+
/>
|
|
387
|
+
}
|
|
388
|
+
/>
|
|
389
|
+
</Field>
|
|
390
|
+
{finalOnAltText && (
|
|
391
|
+
<Dialog.Actions>
|
|
392
|
+
<Dialog.Close>Cancel</Dialog.Close>
|
|
393
|
+
<Button
|
|
394
|
+
emphasis="primary"
|
|
395
|
+
onClick={() => {
|
|
396
|
+
skipCloseResetRef.current = true;
|
|
397
|
+
finalOnAltText(innerAltText);
|
|
398
|
+
setOpen(false);
|
|
399
|
+
}}
|
|
400
|
+
>
|
|
401
|
+
Save
|
|
402
|
+
</Button>
|
|
403
|
+
</Dialog.Actions>
|
|
404
|
+
)}
|
|
405
|
+
</Dialog.Content>
|
|
406
|
+
</Dialog>
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
294
410
|
export function ImageUploaderEmptyControls({
|
|
295
411
|
children,
|
|
296
412
|
className,
|
|
@@ -317,5 +433,6 @@ export const ImageUploader = Object.assign(ImageUploaderPrebuilt, {
|
|
|
317
433
|
FileButton: ImageUploaderFileButton,
|
|
318
434
|
RemoveButton: ImageUploaderRemoveButton,
|
|
319
435
|
Display: ImageUploaderDisplay,
|
|
436
|
+
AltText: ImageUploaderAltText,
|
|
320
437
|
EmptyControls: ImageUploaderEmptyControls,
|
|
321
438
|
});
|