tycho-components 0.47.2 → 0.48.2

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.
@@ -1,3 +1,4 @@
1
+ import { type DropzoneMessages } from '../AppDropzoneShell';
1
2
  import type { UploadedFile } from './UploadedFile';
2
3
  type Props = {
3
4
  folder: string;
@@ -5,7 +6,7 @@ type Props = {
5
6
  onSuccess: (response: UploadedFile) => void;
6
7
  onError?: (err: any) => void;
7
8
  accept: Record<string, string[]>;
8
- messages?: Partial<Record<'label.dropzone' | 'label.uploaded.file', string>>;
9
+ messages?: DropzoneMessages;
9
10
  onDrop?: () => void;
10
11
  /** Initial value for the keep-original-name checkbox (default false). */
11
12
  keepName?: boolean;
@@ -1,13 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useRef } from 'react';
3
- import { useTranslation } from 'react-i18next';
4
- import AppModal from '../AppModal';
3
+ import { AppDropzoneModal, } from '../AppDropzoneShell';
5
4
  import AppDropzoneBody from './AppDropzoneBody';
6
5
  export default function AppDropzone({ folder, onClose, onSuccess, onError, accept, messages, onDrop, keepName, title, alternativeButton, disableConfirm = false, }) {
7
- const { t } = useTranslation('upload');
8
6
  const bodyRef = useRef(null);
9
- const handleConfirm = () => {
10
- bodyRef.current?.upload();
11
- };
12
- return (_jsx(AppModal, { title: title || t('modal.title'), className: "modal-upload", close: onClose, confirm: handleConfirm, disableConfirm: disableConfirm, alternativeButton: alternativeButton, children: _jsx(AppDropzoneBody, { ref: bodyRef, folder: folder, onSuccess: onSuccess, onError: onError, accept: accept, messages: messages, onDrop: onDrop, keepName: keepName, showConfirmButton: false }) }));
7
+ return (_jsx(AppDropzoneModal, { title: title, onClose: onClose, bodyRef: bodyRef, className: "modal-upload", disableConfirm: disableConfirm, alternativeButton: alternativeButton, children: _jsx(AppDropzoneBody, { ref: bodyRef, folder: folder, onSuccess: onSuccess, onError: onError, accept: accept, messages: messages, onDrop: onDrop, keepName: keepName, showConfirmButton: false }) }));
13
8
  }
@@ -1,11 +1,11 @@
1
+ import { type DropzoneMessages } from '../AppDropzoneShell';
1
2
  import { UploadedFile } from './UploadedFile';
2
- import './style.scss';
3
3
  export type AppDropzoneBodyProps = {
4
4
  folder: string;
5
5
  onSuccess: (response: UploadedFile) => void;
6
6
  onError?: (err: any) => void;
7
7
  accept: Record<string, string[]>;
8
- messages?: Partial<Record<'label.dropzone' | 'label.uploaded.file', string>>;
8
+ messages?: DropzoneMessages;
9
9
  onDrop?: () => void;
10
10
  /** Initial value for the keep-original-name checkbox (default false). */
11
11
  keepName?: boolean;
@@ -1,16 +1,15 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Checkbox from '@mui/material/Checkbox';
3
3
  import FormControlLabel from '@mui/material/FormControlLabel';
4
4
  import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState, } from 'react';
5
- import Dropzone from 'react-dropzone';
6
5
  import { useTranslation } from 'react-i18next';
7
6
  import { Button } from 'tycho-storybook';
8
7
  import CommonContext from '../../configs/CommonContext';
9
8
  import { toastLoading } from '../../configs/store/actions';
10
9
  import { useMessageUtils } from '../../configs/useMessageUtils';
11
10
  import AppModalConfirm from '../AppModal/AppModalConfirm';
11
+ import { DropzoneShell, useDropzoneMessages, } from '../AppDropzoneShell';
12
12
  import UploadService from './UploadService';
13
- import './style.scss';
14
13
  function isFileAlreadyExistsError(err) {
15
14
  return (err?.response?.status === 409 &&
16
15
  err?.response?.data?.description === 'upload.file.already.exists');
@@ -19,6 +18,7 @@ function AppDropzoneBodyInner({ folder, onSuccess, onError, accept, messages, on
19
18
  const { t } = useTranslation('upload');
20
19
  const { state, dispatch } = useContext(CommonContext);
21
20
  const { dispatchError } = useMessageUtils();
21
+ const resolveMessage = useDropzoneMessages(messages);
22
22
  const [file, setFile] = useState();
23
23
  const [preview, setPreview] = useState();
24
24
  const [keepOriginalName, setKeepOriginalName] = useState(keepName);
@@ -101,8 +101,6 @@ function AppDropzoneBodyInner({ folder, onSuccess, onError, accept, messages, on
101
101
  upload: () => upload(false),
102
102
  }));
103
103
  const handleDrop = (files) => {
104
- if (files.length === 0)
105
- return;
106
104
  setFile(files[0]);
107
105
  onDrop?.();
108
106
  };
@@ -113,11 +111,7 @@ function AppDropzoneBodyInner({ folder, onSuccess, onError, accept, messages, on
113
111
  setReplaceConfirmOpen(false);
114
112
  upload(true);
115
113
  };
116
- const resolveMessage = (key) => {
117
- const customMessage = messages?.[key]?.trim();
118
- return customMessage || t(key);
119
- };
120
- return (_jsxs("div", { className: "dropzone-container", children: [!file && (_jsx(Dropzone, { onDrop: (acceptedFiles) => handleDrop(acceptedFiles), accept: accept, maxFiles: 1, children: ({ getRootProps, getInputProps }) => (_jsxs("div", { ...getRootProps(), className: "dropzone", children: [_jsx("input", { ...getInputProps() }), _jsx("span", { children: resolveMessage('label.dropzone') })] })) })), file && (_jsxs("div", { className: "uploaded-file", children: [preview && _jsx("img", { className: "preview", src: preview, alt: "" }), !preview && (_jsxs(_Fragment, { children: [_jsx("b", { children: resolveMessage('label.uploaded.file') }), _jsx("span", { children: file.name })] })), showConfirmButton && (_jsxs("div", { className: "buttons", children: [_jsx(Button, { onClick: handleCancel, text: t('common:button.cancel'), size: "small", mode: "outlined", color: "danger" }), _jsx(Button, { onClick: () => upload(false), text: t('common:button.confirm'), size: "small", mode: "outlined", disabled: disableConfirm })] }))] })), _jsx(FormControlLabel, { className: "keep-name-checkbox", control: _jsx(Checkbox, { checked: keepOriginalName, onChange: (_, checked) => setKeepOriginalName(checked) }), label: t('label.keep.original.name') }), replaceConfirmOpen && (_jsx(AppModalConfirm, { title: t('replace.confirm.title'), subtitle: t('replace.confirm.subtitle', {
114
+ return (_jsxs(_Fragment, { children: [_jsx(DropzoneShell, { accept: accept, messages: messages, onDrop: handleDrop, hasSelection: Boolean(file), maxFiles: 1, selection: file ? (_jsxs(_Fragment, { children: [preview && _jsx("img", { className: "preview", src: preview, alt: "" }), !preview && (_jsxs(_Fragment, { children: [_jsx("b", { children: resolveMessage('label.uploaded.file') }), _jsx("span", { children: file.name })] })), showConfirmButton && (_jsxs("div", { className: "buttons", children: [_jsx(Button, { onClick: handleCancel, text: t('common:button.cancel'), size: "small", mode: "outlined", color: "danger" }), _jsx(Button, { onClick: () => upload(false), text: t('common:button.confirm'), size: "small", mode: "outlined", disabled: disableConfirm })] }))] })) : null, after: _jsx(FormControlLabel, { className: "keep-name-checkbox", control: _jsx(Checkbox, { checked: keepOriginalName, onChange: (_, checked) => setKeepOriginalName(checked) }), label: t('label.keep.original.name') }) }), replaceConfirmOpen && (_jsx(AppModalConfirm, { title: t('replace.confirm.title'), subtitle: t('replace.confirm.subtitle', {
121
115
  0: fileRef.current?.name ?? '',
122
116
  }), onClose: () => setReplaceConfirmOpen(false), onConfirm: handleReplaceConfirm }))] }));
123
117
  }
@@ -1,70 +1,3 @@
1
- .dropzone-container {
2
- display: flex;
3
- flex-direction: column;
4
- align-items: stretch;
5
- justify-content: center;
6
- gap: var(--spacing-200);
7
- box-sizing: border-box;
8
- width: 100%;
9
- max-width: 100%;
10
- min-width: 0;
11
- overflow: hidden;
12
-
13
- .dropzone {
14
- display: flex;
15
- align-items: center;
16
- justify-content: center;
17
- text-align: center;
18
- box-sizing: border-box;
19
- width: 100%;
20
- max-width: 100%;
21
- min-width: 0;
22
- padding: var(--spacing-700);
23
- border-radius: var(--radius-100);
24
- border: 2px dashed var(--border-subtle-3);
25
- background-color: var(--layer-layer-2);
26
- cursor: pointer;
27
- }
28
-
29
- .preview {
30
- display: block;
31
- margin: 0 auto;
32
- width: 100%;
33
- max-width: 400px;
34
- height: auto;
35
- aspect-ratio: 1;
36
- object-fit: contain;
37
- }
38
-
39
- .uploaded-file {
40
- display: flex;
41
- flex-direction: column;
42
- align-items: center;
43
- box-sizing: border-box;
44
- width: 100%;
45
- max-width: 100%;
46
- min-width: 0;
47
- text-align: center;
48
- border: 1px solid var(--border-subtle-3);
49
- border-radius: var(--radius-100);
50
- padding: var(--spacing-200);
51
- gap: var(--spacing-200);
52
- overflow-wrap: anywhere;
53
-
54
- .buttons {
55
- display: flex;
56
- gap: var(--spacing-200);
57
- margin-top: var(--spacing-200);
58
- }
59
- }
60
-
61
- .keep-name-checkbox {
62
- align-self: flex-start;
63
- max-width: 100%;
64
- min-width: 0;
65
-
66
- > .MuiCheckbox-root {
67
- padding: 0px 8px;
68
- }
69
- }
70
- }
1
+ // Deprecated local entry: styles live in AppDropzoneShell/style.scss
2
+ // and are loaded by DropzoneShell. Kept so existing @import paths do not break.
3
+ @forward '../AppDropzoneShell/style';
@@ -1,9 +1,10 @@
1
- import './style.scss';
1
+ import { type DropzoneMessages } from '../AppDropzoneShell';
2
2
  import type { UploadedFileLocal } from './UploadedFileLocal';
3
3
  export type AppDropzoneBodyLocalProps = {
4
4
  onSuccess: (files: UploadedFileLocal[]) => void;
5
5
  onError?: (err: unknown) => void;
6
6
  accept: Record<string, string[]>;
7
+ messages?: DropzoneMessages;
7
8
  onDrop?: () => void;
8
9
  /** When false, the confirm button is hidden (e.g. when used inside modal with its own confirm) */
9
10
  showConfirmButton?: boolean;
@@ -1,11 +1,11 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
3
- import Dropzone from 'react-dropzone';
4
3
  import { useTranslation } from 'react-i18next';
5
4
  import { Button } from 'tycho-storybook';
6
- import './style.scss';
7
- function AppDropzoneBodyLocalInner({ onSuccess, onError, accept, onDrop, showConfirmButton = true, }, ref) {
5
+ import { DropzoneShell, useDropzoneMessages, } from '../AppDropzoneShell';
6
+ function AppDropzoneBodyLocalInner({ onSuccess, onError, accept, messages, onDrop, showConfirmButton = true, }, ref) {
8
7
  const { t } = useTranslation('upload');
8
+ const resolveMessage = useDropzoneMessages(messages);
9
9
  const [files, setFiles] = useState([]);
10
10
  const [isUploading, setIsUploading] = useState(false);
11
11
  const filesRef = useRef([]);
@@ -33,8 +33,6 @@ function AppDropzoneBodyLocalInner({ onSuccess, onError, accept, onDrop, showCon
33
33
  upload,
34
34
  }));
35
35
  const handleDrop = (acceptedFiles) => {
36
- if (acceptedFiles.length === 0)
37
- return;
38
36
  setFiles((prev) => [...prev, ...acceptedFiles]);
39
37
  onDrop?.();
40
38
  };
@@ -42,14 +40,14 @@ function AppDropzoneBodyLocalInner({ onSuccess, onError, accept, onDrop, showCon
42
40
  const removeAt = (index) => {
43
41
  setFiles((prev) => prev.filter((_, i) => i !== index));
44
42
  };
45
- return (_jsxs("div", { className: "dropzone-container-local", children: [files.length === 0 && (_jsx(Dropzone, { onDrop: handleDrop, accept: accept, multiple: true, children: ({ getRootProps, getInputProps }) => (_jsxs("div", { ...getRootProps(), className: "dropzone-local", children: [_jsx("input", { ...getInputProps() }), _jsx("span", { children: t('label.dropzone') })] })) })), files.length > 0 && (_jsxs("div", { className: "uploaded-message-local", children: [_jsx("b", { children: t('label.uploaded.file') }), _jsx("ul", { className: "uploaded-file-list-local", children: files.map((file, index) => (_jsxs("li", { children: [_jsx("span", { children: file.name }), _jsx("button", { type: "button", className: "uploaded-file-remove-local", onClick: (e) => {
46
- e.stopPropagation();
47
- removeAt(index);
48
- }, "aria-label": t('common:button.remove', {
49
- defaultValue: 'Remove',
50
- }), children: "\u00D7" })] }, `${file.name}-${file.size}-${index}`))) }), _jsx("button", { type: "button", className: "uploaded-clear-all-local", onClick: clearFiles, children: t('label.clear.all', { defaultValue: 'Clear all' }) }), _jsx("b", { children: t('label.confirm') })] })), files.length > 0 && showConfirmButton && (_jsx(Button, { onClick: () => {
51
- void upload();
52
- }, text: t('common:button.confirm') }))] }));
43
+ return (_jsx(DropzoneShell, { accept: accept, messages: messages, onDrop: handleDrop, hasSelection: files.length > 0, multiple: true, selection: _jsxs(_Fragment, { children: [_jsx("b", { children: resolveMessage('label.uploaded.file') }), _jsx("ul", { className: "uploaded-file-list", children: files.map((file, index) => (_jsxs("li", { children: [_jsx("span", { children: file.name }), _jsx("button", { type: "button", className: "uploaded-file-remove", onClick: (e) => {
44
+ e.stopPropagation();
45
+ removeAt(index);
46
+ }, "aria-label": t('common:button.remove', {
47
+ defaultValue: 'Remove',
48
+ }), children: "\u00D7" })] }, `${file.name}-${file.size}-${index}`))) }), _jsx("button", { type: "button", className: "uploaded-clear-all", onClick: clearFiles, children: t('label.clear.all') }), _jsx("b", { children: t('label.confirm') }), showConfirmButton && (_jsx(Button, { onClick: () => {
49
+ void upload();
50
+ }, text: t('common:button.confirm') }))] }) }));
53
51
  }
54
52
  const AppDropzoneBodyLocal = forwardRef(AppDropzoneBodyLocalInner);
55
53
  export default AppDropzoneBodyLocal;
@@ -1,9 +1,11 @@
1
+ import { type DropzoneMessages } from '../AppDropzoneShell';
1
2
  import type { UploadedFileLocal } from './UploadedFileLocal';
2
3
  type AppDropzoneLocalProps = {
3
4
  onClose: () => void;
4
5
  onSuccess: (files: UploadedFileLocal[]) => void;
5
6
  onError?: (err: unknown) => void;
6
7
  accept: Record<string, string[]>;
8
+ messages?: DropzoneMessages;
7
9
  onDrop?: () => void;
8
10
  title?: string;
9
11
  alternativeButton?: {
@@ -11,5 +13,5 @@ type AppDropzoneLocalProps = {
11
13
  action: () => void;
12
14
  };
13
15
  };
14
- export default function AppDropzoneLocal({ onClose, onSuccess, onError, accept, onDrop, title, alternativeButton, }: AppDropzoneLocalProps): import("react").JSX.Element;
16
+ export default function AppDropzoneLocal({ onClose, onSuccess, onError, accept, messages, onDrop, title, alternativeButton, }: AppDropzoneLocalProps): import("react").JSX.Element;
15
17
  export {};
@@ -1,13 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useRef } from 'react';
3
- import { useTranslation } from 'react-i18next';
4
- import AppModal from '../AppModal';
3
+ import { AppDropzoneModal, } from '../AppDropzoneShell';
5
4
  import AppDropzoneBodyLocal from './AppDropzoneBodyLocal';
6
- export default function AppDropzoneLocal({ onClose, onSuccess, onError, accept, onDrop, title, alternativeButton, }) {
7
- const { t } = useTranslation('upload');
5
+ export default function AppDropzoneLocal({ onClose, onSuccess, onError, accept, messages, onDrop, title, alternativeButton, }) {
8
6
  const bodyRef = useRef(null);
9
- const handleConfirm = () => {
10
- void bodyRef.current?.upload();
11
- };
12
- return (_jsx(AppModal, { title: title || t('modal.title'), className: "modal-upload-local", close: onClose, confirm: handleConfirm, alternativeButton: alternativeButton, children: _jsx(AppDropzoneBodyLocal, { ref: bodyRef, onSuccess: onSuccess, onError: onError, accept: accept, onDrop: onDrop, showConfirmButton: false }) }));
7
+ return (_jsx(AppDropzoneModal, { title: title, onClose: onClose, bodyRef: bodyRef, className: "modal-upload-local", alternativeButton: alternativeButton, children: _jsx(AppDropzoneBodyLocal, { ref: bodyRef, onSuccess: onSuccess, onError: onError, accept: accept, messages: messages, onDrop: onDrop, showConfirmButton: false }) }));
13
8
  }
@@ -1,75 +1,3 @@
1
- .dropzone-container-local {
2
- display: flex;
3
- align-items: center;
4
- justify-content: center;
5
-
6
- .dropzone-local {
7
- display: flex;
8
- align-items: center;
9
- justify-content: center;
10
- text-align: center;
11
- width: 100%;
12
- padding: var(--spacing-700);
13
- border-radius: var(--radius-100);
14
- border: 2px dashed var(--border-subtle-3);
15
- background-color: var(--layer-layer-2);
16
- cursor: pointer;
17
- }
18
-
19
- .preview-local {
20
- margin: 0 auto;
21
- width: 400px;
22
- height: 400px;
23
- }
24
-
25
- .uploaded-message-local {
26
- display: flex;
27
- flex-direction: column;
28
- width: 100%;
29
- gap: 16px;
30
- text-align: center;
31
- border: 1px solid var(--border-subtle-3);
32
- border-radius: var(--radius-100);
33
- padding: 16px;
34
- margin-top: var(--spacing-400);
35
- }
36
-
37
- .uploaded-file-list-local {
38
- list-style: none;
39
- margin: 0;
40
- padding: 0;
41
- text-align: left;
42
- border: var(--border-default);
43
- padding: 8px;
44
- border-radius: var(--radius-100);
45
-
46
- li {
47
- display: flex;
48
- align-items: center;
49
- justify-content: space-between;
50
- gap: 8px;
51
- padding: 4px 0;
52
- }
53
- }
54
-
55
- .uploaded-file-remove-local {
56
- flex-shrink: 0;
57
- border: none;
58
- background: transparent;
59
- cursor: pointer;
60
- font-size: 1.25rem;
61
- line-height: 1;
62
- padding: 0 4px;
63
- color: var(--text-secondary, inherit);
64
- }
65
-
66
- .uploaded-clear-all-local {
67
- align-self: center;
68
- border: none;
69
- background: transparent;
70
- cursor: pointer;
71
- text-decoration: underline;
72
- font: inherit;
73
- color: var(--text-link, inherit);
74
- }
75
- }
1
+ // Deprecated local entry: styles live in AppDropzoneShell/style.scss
2
+ // and are loaded by DropzoneShell. Kept so existing @import paths do not break.
3
+ @forward '../AppDropzoneShell/style';
@@ -0,0 +1,24 @@
1
+ import type { ReactNode } from 'react';
2
+ export type AppDropzoneUploadRef = {
3
+ upload: () => void | Promise<void>;
4
+ };
5
+ export type AppDropzoneModalProps = {
6
+ onClose: () => void;
7
+ /** Ref to the dropzone body that exposes imperative `upload()`. */
8
+ bodyRef: {
9
+ current: AppDropzoneUploadRef | null;
10
+ };
11
+ title?: string;
12
+ className?: string;
13
+ disableConfirm?: boolean;
14
+ alternativeButton?: {
15
+ title: string;
16
+ action: () => void;
17
+ };
18
+ children: ReactNode;
19
+ };
20
+ /**
21
+ * Shared AppModal + imperative confirm wiring for dropzone strategies.
22
+ * The body exposes `upload()` via `bodyRef`.
23
+ */
24
+ export default function AppDropzoneModal({ onClose, bodyRef, title, className, disableConfirm, alternativeButton, children, }: AppDropzoneModalProps): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useTranslation } from 'react-i18next';
3
+ import AppModal from '../AppModal';
4
+ /**
5
+ * Shared AppModal + imperative confirm wiring for dropzone strategies.
6
+ * The body exposes `upload()` via `bodyRef`.
7
+ */
8
+ export default function AppDropzoneModal({ onClose, bodyRef, title, className, disableConfirm, alternativeButton, children, }) {
9
+ const { t } = useTranslation('upload');
10
+ return (_jsx(AppModal, { title: title || t('modal.title'), className: className, close: onClose, confirm: () => {
11
+ void bodyRef.current?.upload();
12
+ }, disableConfirm: disableConfirm, alternativeButton: alternativeButton, children: children }));
13
+ }
@@ -0,0 +1,17 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type DropzoneMessages } from './dropzoneMessages';
3
+ import './style.scss';
4
+ export type DropzoneShellProps = {
5
+ accept: Record<string, string[]>;
6
+ messages?: DropzoneMessages;
7
+ onDrop: (files: File[]) => void;
8
+ hasSelection: boolean;
9
+ /** When true, react-dropzone accepts multiple files. */
10
+ multiple?: boolean;
11
+ maxFiles?: number;
12
+ /** Content rendered inside the shared selected-state panel. */
13
+ selection?: ReactNode;
14
+ /** Extra content below the dropzone / selected panel (e.g. keep-name). */
15
+ after?: ReactNode;
16
+ };
17
+ export default function DropzoneShell({ accept, messages, onDrop, hasSelection, multiple, maxFiles, selection, after, }: DropzoneShellProps): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Dropzone from 'react-dropzone';
3
+ import { useDropzoneMessages, } from './dropzoneMessages';
4
+ import './style.scss';
5
+ export default function DropzoneShell({ accept, messages, onDrop, hasSelection, multiple = false, maxFiles, selection, after, }) {
6
+ const resolveMessage = useDropzoneMessages(messages);
7
+ return (_jsxs("div", { className: "dropzone-container", children: [!hasSelection && (_jsx(Dropzone, { onDrop: (acceptedFiles) => {
8
+ if (acceptedFiles.length === 0)
9
+ return;
10
+ onDrop(acceptedFiles);
11
+ }, accept: accept, multiple: multiple, maxFiles: maxFiles, children: ({ getRootProps, getInputProps }) => (_jsxs("div", { ...getRootProps(), className: "dropzone", children: [_jsx("input", { ...getInputProps() }), _jsx("span", { children: resolveMessage('label.dropzone') })] })) })), hasSelection && selection != null && (_jsx("div", { className: "uploaded-file", children: selection })), after] }));
12
+ }
@@ -0,0 +1,4 @@
1
+ export type DropzoneMessageKey = 'label.dropzone' | 'label.uploaded.file';
2
+ export type DropzoneMessages = Partial<Record<DropzoneMessageKey, string>>;
3
+ export declare function resolveDropzoneMessage(t: (key: string) => string, messages: DropzoneMessages | undefined, key: DropzoneMessageKey): string;
4
+ export declare function useDropzoneMessages(messages?: DropzoneMessages): (key: DropzoneMessageKey) => string;
@@ -0,0 +1,9 @@
1
+ import { useTranslation } from 'react-i18next';
2
+ export function resolveDropzoneMessage(t, messages, key) {
3
+ const customMessage = messages?.[key]?.trim();
4
+ return customMessage || t(key);
5
+ }
6
+ export function useDropzoneMessages(messages) {
7
+ const { t } = useTranslation('upload');
8
+ return (key) => resolveDropzoneMessage(t, messages, key);
9
+ }
@@ -0,0 +1,5 @@
1
+ export { default as AppDropzoneModal } from './AppDropzoneModal';
2
+ export type { AppDropzoneModalProps, AppDropzoneUploadRef, } from './AppDropzoneModal';
3
+ export { default as DropzoneShell } from './DropzoneShell';
4
+ export type { DropzoneShellProps } from './DropzoneShell';
5
+ export { resolveDropzoneMessage, useDropzoneMessages, type DropzoneMessageKey, type DropzoneMessages, } from './dropzoneMessages';
@@ -0,0 +1,3 @@
1
+ export { default as AppDropzoneModal } from './AppDropzoneModal';
2
+ export { default as DropzoneShell } from './DropzoneShell';
3
+ export { resolveDropzoneMessage, useDropzoneMessages, } from './dropzoneMessages';
@@ -0,0 +1,109 @@
1
+ .dropzone-container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: stretch;
5
+ justify-content: center;
6
+ gap: var(--spacing-200);
7
+ box-sizing: border-box;
8
+ width: 100%;
9
+ max-width: 100%;
10
+ min-width: 0;
11
+ overflow: hidden;
12
+
13
+ .dropzone {
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ text-align: center;
18
+ box-sizing: border-box;
19
+ width: 100%;
20
+ max-width: 100%;
21
+ min-width: 0;
22
+ padding: var(--spacing-700);
23
+ border-radius: var(--radius-100);
24
+ border: 2px dashed var(--border-subtle-3);
25
+ background-color: var(--layer-layer-2);
26
+ cursor: pointer;
27
+ }
28
+
29
+ .preview {
30
+ display: block;
31
+ margin: 0 auto;
32
+ width: 100%;
33
+ max-width: 400px;
34
+ height: auto;
35
+ aspect-ratio: 1;
36
+ object-fit: contain;
37
+ }
38
+
39
+ .uploaded-file {
40
+ display: flex;
41
+ flex-direction: column;
42
+ align-items: center;
43
+ box-sizing: border-box;
44
+ width: 100%;
45
+ max-width: 100%;
46
+ min-width: 0;
47
+ text-align: center;
48
+ border: 1px solid var(--border-subtle-3);
49
+ border-radius: var(--radius-100);
50
+ padding: var(--spacing-200);
51
+ gap: var(--spacing-200);
52
+ overflow-wrap: anywhere;
53
+
54
+ .buttons {
55
+ display: flex;
56
+ gap: var(--spacing-200);
57
+ margin-top: var(--spacing-200);
58
+ }
59
+ }
60
+
61
+ .uploaded-file-list {
62
+ list-style: none;
63
+ margin: 0;
64
+ width: 100%;
65
+ text-align: left;
66
+ border: var(--border-default);
67
+ padding: var(--spacing-100);
68
+ border-radius: var(--radius-100);
69
+
70
+ li {
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: space-between;
74
+ gap: var(--spacing-100);
75
+ padding: var(--spacing-050) 0;
76
+ }
77
+ }
78
+
79
+ .uploaded-file-remove {
80
+ flex-shrink: 0;
81
+ border: none;
82
+ background: transparent;
83
+ cursor: pointer;
84
+ font-size: 1.25rem;
85
+ line-height: 1;
86
+ padding: 0 var(--spacing-050);
87
+ color: var(--text-secondary, inherit);
88
+ }
89
+
90
+ .uploaded-clear-all {
91
+ align-self: center;
92
+ border: none;
93
+ background: transparent;
94
+ cursor: pointer;
95
+ text-decoration: underline;
96
+ font: inherit;
97
+ color: var(--text-link, inherit);
98
+ }
99
+
100
+ .keep-name-checkbox {
101
+ align-self: flex-start;
102
+ max-width: 100%;
103
+ min-width: 0;
104
+
105
+ > .MuiCheckbox-root {
106
+ padding: 0 var(--spacing-100);
107
+ }
108
+ }
109
+ }
@@ -3,6 +3,7 @@ export declare const UploadTexts: {
3
3
  'label.dropzone': string;
4
4
  'label.uploaded.file': string;
5
5
  'label.confirm': string;
6
+ 'label.clear.all': string;
6
7
  'label.keep.original.name': string;
7
8
  'modal.title': string;
8
9
  'error.uploading.image': string;
@@ -20,6 +21,7 @@ export declare const UploadTexts: {
20
21
  'label.dropzone': string;
21
22
  'label.uploaded.file': string;
22
23
  'label.confirm': string;
24
+ 'label.clear.all': string;
23
25
  'label.keep.original.name': string;
24
26
  'modal.title': string;
25
27
  'error.uploading.image': string;
@@ -37,6 +39,7 @@ export declare const UploadTexts: {
37
39
  'label.dropzone': string;
38
40
  'label.uploaded.file': string;
39
41
  'label.confirm': string;
42
+ 'label.clear.all': string;
40
43
  'label.keep.original.name': string;
41
44
  'modal.title': string;
42
45
  'error.uploading.image': string;
@@ -3,6 +3,7 @@ export const UploadTexts = {
3
3
  'label.dropzone': 'Drag and drop or click to upload a file',
4
4
  'label.uploaded.file': 'You are uploading the following file:',
5
5
  'label.confirm': 'Press confirm to continue',
6
+ 'label.clear.all': 'Clear all',
6
7
  'label.keep.original.name': 'Keep original file name',
7
8
  'modal.title': 'Upload a file',
8
9
  'error.uploading.image': 'An error occurred while uploading a file. Contact the administrator.',
@@ -20,6 +21,7 @@ export const UploadTexts = {
20
21
  'label.dropzone': 'Arraste e solte ou clique para enviar um arquivo.',
21
22
  'label.uploaded.file': 'Você está enviando o seguinte arquivo:',
22
23
  'label.confirm': 'Pressione confirmar para continuar',
24
+ 'label.clear.all': 'Limpar tudo',
23
25
  'label.keep.original.name': 'Manter o nome original do arquivo',
24
26
  'modal.title': 'Upload de arquivo',
25
27
  'error.uploading.image': 'Ocorreu um erro ao enviar o arquivo. Entre em contato com o administrador.',
@@ -37,6 +39,7 @@ export const UploadTexts = {
37
39
  'label.dropzone': 'Trascina e rilascia o clicca per caricare un file',
38
40
  'label.uploaded.file': 'Stai caricando il seguente file:',
39
41
  'label.confirm': 'Premi conferma per continuare',
42
+ 'label.clear.all': 'Cancella tutto',
40
43
  'label.keep.original.name': 'Mantieni il nome originale del file',
41
44
  'modal.title': 'Carica un file',
42
45
  'error.uploading.image': "Si è verificato un errore durante il caricamento del file. Contatta l'amministratore.",
@@ -9,7 +9,7 @@ import "./style.scss";
9
9
  export default function AccessUser({ mode, resourceUid, user, onClose, onReload, setItems, }) {
10
10
  const { t } = useTranslation("access");
11
11
  const { getCorpus, hasCorpus } = useCorpusUtils();
12
- const showPortal = mode !== "corpus" || (hasCorpus() && getCorpus().opened);
12
+ const showPortal = mode === "corpus" && hasCorpus() && getCorpus().opened;
13
13
  const handleChangeAccess = (r) => {
14
14
  setItems((prevState) => {
15
15
  if (!prevState)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tycho-components",
3
3
  "private": false,
4
- "version": "0.47.2",
4
+ "version": "0.48.2",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {