@mittwald/flow-react-components 1.3.0-next.14 → 1.3.0-next.16

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.
@@ -5,6 +5,7 @@ import { flowComponent } from "../../lib/componentFactory/flowComponent.mjs";
5
5
  import { useFieldComponent } from "../../lib/hooks/useFieldComponent.mjs";
6
6
  import { IllustratedMessage } from "../IllustratedMessage/IllustratedMessage.mjs";
7
7
  import FileDropZone_module_default from "./FileDropZone.module.mjs";
8
+ import { getAcceptedFiles } from "../../lib/files/acceptedFiles.mjs";
8
9
  import clsx from "clsx";
9
10
  import { useRef } from "react";
10
11
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -39,8 +40,7 @@ var FileDropZone = flowComponent("FileDropZone", (props) => {
39
40
  };
40
41
  const onDropHandler = async (event) => {
41
42
  if (isReadOnly) return;
42
- const fileDropItems = event.items.filter((file) => file.kind === "file");
43
- const files = await Promise.all(fileDropItems.filter((f) => !accept || accept?.includes(f.type)).map(async (f) => await f.getFile()));
43
+ const files = await getAcceptedFiles(event.items, accept);
44
44
  if (files.length > 0) {
45
45
  const fileTransfer = new DataTransfer();
46
46
  for (const file of multiple ? files : [files[0]]) if (file) fileTransfer.items.add(file);
@@ -1 +1 @@
1
- {"version":3,"file":"FileDropZone.mjs","names":[],"sources":["../../../../../../../src/components/FileDropZone/FileDropZone.tsx"],"sourcesContent":["import { type FC, type PropsWithChildren, useRef } from \"react\";\nimport * as Aria from \"react-aria-components\";\nimport { IllustratedMessage } from \"@/components/IllustratedMessage\";\nimport type { PropsWithClassName } from \"@/lib/types/props\";\nimport styles from \"./FileDropZone.module.scss\";\nimport clsx from \"clsx\";\nimport type { FileInputOnChangeHandler } from \"@/components/FileField/components/FileInput\";\nimport type { PropsContext } from \"@/lib/propsContext\";\nimport { PropsContextProvider } from \"@/lib/propsContext\";\nimport {\n flowComponent,\n type FlowComponentProps,\n} from \"@/lib/componentFactory/flowComponent\";\nimport type { DropEvent, FocusableElement } from \"@react-types/shared\";\nimport { useFieldComponent } from \"@/lib/hooks/useFieldComponent\";\n\nexport interface FileDropZoneProps\n extends\n PropsWithClassName,\n FlowComponentProps<FocusableElement>,\n PropsWithChildren,\n Pick<Aria.InputProps, \"accept\" | \"multiple\" | \"name\">,\n Pick<Aria.DropZoneProps, \"isDisabled\"> {\n /** Called with the dropped or selected files whenever the selection changes. */\n onChange?: FileInputOnChangeHandler;\n /** Whether the component is read only. */\n isReadOnly?: boolean;\n}\n\n/** @flr-generate all */\nexport const FileDropZone: FC<FileDropZoneProps> = flowComponent(\n \"FileDropZone\",\n (props) => {\n const {\n multiple,\n accept,\n className,\n onChange: onChangeDropZone,\n children,\n name,\n isDisabled,\n isReadOnly,\n } = props;\n\n const {\n FieldErrorView,\n FieldErrorCaptureContext,\n fieldProps,\n fieldPropsContext,\n } = useFieldComponent(props, \"FileDropZone\");\n\n const fileFieldRef = useRef<HTMLInputElement>(null);\n const rootClassName = clsx(\n styles.fileDropZone,\n isDisabled && styles.disabled,\n className,\n );\n\n const propsContext: PropsContext = {\n ...fieldPropsContext,\n IllustratedMessage: {\n FileField: {\n name,\n onChange: onChangeDropZone,\n ref: fileFieldRef,\n accept: accept,\n multiple: multiple,\n Button: { variant: \"outline\", color: \"dark\" },\n isDisabled,\n isReadOnly,\n },\n Heading: {\n className: styles.heading,\n },\n Icon: { className: styles.icon },\n Text: { className: styles.text },\n },\n };\n\n const onDropHandler = async (event: DropEvent) => {\n if (isReadOnly) {\n return;\n }\n\n const fileDropItems = event.items.filter(\n (file) => file.kind === \"file\",\n ) as Aria.FileDropItem[];\n\n const files = await Promise.all(\n fileDropItems\n .filter((f) => !accept || accept?.includes(f.type))\n .map(async (f) => await f.getFile()),\n );\n\n if (files.length > 0) {\n const fileTransfer = new DataTransfer();\n for (const file of multiple ? files : [files[0]]) {\n if (file) {\n fileTransfer.items.add(file);\n }\n }\n\n onChangeDropZone?.(fileTransfer.files);\n if (fileFieldRef.current) {\n fileFieldRef.current.files = fileTransfer.files;\n }\n }\n };\n\n return (\n <div {...fieldProps}>\n <FieldErrorCaptureContext>\n <PropsContextProvider props={propsContext}>\n <Aria.DropZone\n className={rootClassName}\n onDrop={onDropHandler}\n isDisabled={isDisabled}\n data-readonly={isReadOnly}\n >\n <IllustratedMessage color=\"dark\">{children}</IllustratedMessage>\n </Aria.DropZone>\n </PropsContextProvider>\n </FieldErrorCaptureContext>\n <FieldErrorView />\n </div>\n );\n },\n);\n\nexport default FileDropZone;\n"],"mappings":";;;;;;;;;;;;;AA8BA,IAAa,eAAsC,cACjD,iBACC,UAAU;CACT,MAAM,EACJ,UACA,QACA,WACA,UAAU,kBACV,UACA,MACA,YACA,eACE;CAEJ,MAAM,EACJ,gBACA,0BACA,YACA,sBACE,kBAAkB,OAAO,cAAc;CAE3C,MAAM,eAAe,OAAyB,IAAI;CAClD,MAAM,gBAAgB,KACpB,4BAAO,cACP,cAAc,4BAAO,UACrB,SACF;CAEA,MAAM,eAA6B;EACjC,GAAG;EACH,oBAAoB;GAClB,WAAW;IACT;IACA,UAAU;IACV,KAAK;IACG;IACE;IACV,QAAQ;KAAE,SAAS;KAAW,OAAO;IAAO;IAC5C;IACA;GACF;GACA,SAAS,EACP,WAAW,4BAAO,QACpB;GACA,MAAM,EAAE,WAAW,4BAAO,KAAK;GAC/B,MAAM,EAAE,WAAW,4BAAO,KAAK;EACjC;CACF;CAEA,MAAM,gBAAgB,OAAO,UAAqB;EAChD,IAAI,YACF;EAGF,MAAM,gBAAgB,MAAM,MAAM,QAC/B,SAAS,KAAK,SAAS,MAC1B;EAEA,MAAM,QAAQ,MAAM,QAAQ,IAC1B,cACG,QAAQ,MAAM,CAAC,UAAU,QAAQ,SAAS,EAAE,IAAI,CAAC,CAAC,CAClD,IAAI,OAAO,MAAM,MAAM,EAAE,QAAQ,CAAC,CACvC;EAEA,IAAI,MAAM,SAAS,GAAG;GACpB,MAAM,eAAe,IAAI,aAAa;GACtC,KAAK,MAAM,QAAQ,WAAW,QAAQ,CAAC,MAAM,EAAE,GAC7C,IAAI,MACF,aAAa,MAAM,IAAI,IAAI;GAI/B,mBAAmB,aAAa,KAAK;GACrC,IAAI,aAAa,SACf,aAAa,QAAQ,QAAQ,aAAa;EAE9C;CACF;CAEA,OACE,qBAAC,OAAD;EAAK,GAAI;EAAT,UAAA,CACE,oBAAC,0BAAD,EAAA,UACE,oBAAC,sBAAD;GAAsB,OAAO;GAC3B,UAAA,oBAAC,KAAK,UAAN;IACE,WAAW;IACX,QAAQ;IACI;IACZ,iBAAe;IAEf,UAAA,oBAAC,oBAAD;KAAoB,OAAM;KAAQ;IAA6B,CAAA;GAClD,CAAA;EACK,CAAA,EACE,CAAA,GAC1B,oBAAC,gBAAD,CAAiB,CAAA,CACd;;AAET,CACF"}
1
+ {"version":3,"file":"FileDropZone.mjs","names":[],"sources":["../../../../../../../src/components/FileDropZone/FileDropZone.tsx"],"sourcesContent":["import { type FC, type PropsWithChildren, useRef } from \"react\";\nimport * as Aria from \"react-aria-components\";\nimport { IllustratedMessage } from \"@/components/IllustratedMessage\";\nimport type { PropsWithClassName } from \"@/lib/types/props\";\nimport styles from \"./FileDropZone.module.scss\";\nimport clsx from \"clsx\";\nimport type { FileInputOnChangeHandler } from \"@/components/FileField/components/FileInput\";\nimport type { PropsContext } from \"@/lib/propsContext\";\nimport { PropsContextProvider } from \"@/lib/propsContext\";\nimport {\n flowComponent,\n type FlowComponentProps,\n} from \"@/lib/componentFactory/flowComponent\";\nimport type { DropEvent, FocusableElement } from \"@react-types/shared\";\nimport { useFieldComponent } from \"@/lib/hooks/useFieldComponent\";\nimport { getAcceptedFiles } from \"@/lib/files/acceptedFiles\";\n\nexport interface FileDropZoneProps\n extends\n PropsWithClassName,\n FlowComponentProps<FocusableElement>,\n PropsWithChildren,\n Pick<Aria.InputProps, \"accept\" | \"multiple\" | \"name\">,\n Pick<Aria.DropZoneProps, \"isDisabled\"> {\n /** Called with the dropped or selected files whenever the selection changes. */\n onChange?: FileInputOnChangeHandler;\n /** Whether the component is read only. */\n isReadOnly?: boolean;\n}\n\n/** @flr-generate all */\nexport const FileDropZone: FC<FileDropZoneProps> = flowComponent(\n \"FileDropZone\",\n (props) => {\n const {\n multiple,\n accept,\n className,\n onChange: onChangeDropZone,\n children,\n name,\n isDisabled,\n isReadOnly,\n } = props;\n\n const {\n FieldErrorView,\n FieldErrorCaptureContext,\n fieldProps,\n fieldPropsContext,\n } = useFieldComponent(props, \"FileDropZone\");\n\n const fileFieldRef = useRef<HTMLInputElement>(null);\n const rootClassName = clsx(\n styles.fileDropZone,\n isDisabled && styles.disabled,\n className,\n );\n\n const propsContext: PropsContext = {\n ...fieldPropsContext,\n IllustratedMessage: {\n FileField: {\n name,\n onChange: onChangeDropZone,\n ref: fileFieldRef,\n accept: accept,\n multiple: multiple,\n Button: { variant: \"outline\", color: \"dark\" },\n isDisabled,\n isReadOnly,\n },\n Heading: {\n className: styles.heading,\n },\n Icon: { className: styles.icon },\n Text: { className: styles.text },\n },\n };\n\n const onDropHandler = async (event: DropEvent) => {\n if (isReadOnly) {\n return;\n }\n\n const files = await getAcceptedFiles(event.items, accept);\n\n if (files.length > 0) {\n const fileTransfer = new DataTransfer();\n for (const file of multiple ? files : [files[0]]) {\n if (file) {\n fileTransfer.items.add(file);\n }\n }\n\n onChangeDropZone?.(fileTransfer.files);\n if (fileFieldRef.current) {\n fileFieldRef.current.files = fileTransfer.files;\n }\n }\n };\n\n return (\n <div {...fieldProps}>\n <FieldErrorCaptureContext>\n <PropsContextProvider props={propsContext}>\n <Aria.DropZone\n className={rootClassName}\n onDrop={onDropHandler}\n isDisabled={isDisabled}\n data-readonly={isReadOnly}\n >\n <IllustratedMessage color=\"dark\">{children}</IllustratedMessage>\n </Aria.DropZone>\n </PropsContextProvider>\n </FieldErrorCaptureContext>\n <FieldErrorView />\n </div>\n );\n },\n);\n\nexport default FileDropZone;\n"],"mappings":";;;;;;;;;;;;;;AA+BA,IAAa,eAAsC,cACjD,iBACC,UAAU;CACT,MAAM,EACJ,UACA,QACA,WACA,UAAU,kBACV,UACA,MACA,YACA,eACE;CAEJ,MAAM,EACJ,gBACA,0BACA,YACA,sBACE,kBAAkB,OAAO,cAAc;CAE3C,MAAM,eAAe,OAAyB,IAAI;CAClD,MAAM,gBAAgB,KACpB,4BAAO,cACP,cAAc,4BAAO,UACrB,SACF;CAEA,MAAM,eAA6B;EACjC,GAAG;EACH,oBAAoB;GAClB,WAAW;IACT;IACA,UAAU;IACV,KAAK;IACG;IACE;IACV,QAAQ;KAAE,SAAS;KAAW,OAAO;IAAO;IAC5C;IACA;GACF;GACA,SAAS,EACP,WAAW,4BAAO,QACpB;GACA,MAAM,EAAE,WAAW,4BAAO,KAAK;GAC/B,MAAM,EAAE,WAAW,4BAAO,KAAK;EACjC;CACF;CAEA,MAAM,gBAAgB,OAAO,UAAqB;EAChD,IAAI,YACF;EAGF,MAAM,QAAQ,MAAM,iBAAiB,MAAM,OAAO,MAAM;EAExD,IAAI,MAAM,SAAS,GAAG;GACpB,MAAM,eAAe,IAAI,aAAa;GACtC,KAAK,MAAM,QAAQ,WAAW,QAAQ,CAAC,MAAM,EAAE,GAC7C,IAAI,MACF,aAAa,MAAM,IAAI,IAAI;GAI/B,mBAAmB,aAAa,KAAK;GACrC,IAAI,aAAa,SACf,aAAa,QAAQ,QAAQ,aAAa;EAE9C;CACF;CAEA,OACE,qBAAC,OAAD;EAAK,GAAI;EAAT,UAAA,CACE,oBAAC,0BAAD,EAAA,UACE,oBAAC,sBAAD;GAAsB,OAAO;GAC3B,UAAA,oBAAC,KAAK,UAAN;IACE,WAAW;IACX,QAAQ;IACI;IACZ,iBAAe;IAEf,UAAA,oBAAC,oBAAD;KAAoB,OAAM;KAAQ;IAA6B,CAAA;GAClD,CAAA;EACK,CAAA,EACE,CAAA,GAC1B,oBAAC,gBAAD,CAAiB,CAAA,CACd;;AAET,CACF"}
@@ -0,0 +1,34 @@
1
+ "use client"
2
+ /* */
3
+ //#region src/lib/files/acceptedFiles.ts
4
+ /**
5
+ * Whether a file matches an `accept` attribute value — a comma separated list
6
+ * of MIME types (`image/png`), MIME wildcards (`image/*`) and file extensions
7
+ * (`.png`). A missing or empty list matches everything, the way the attribute
8
+ * itself does.
9
+ *
10
+ * @internal
11
+ */
12
+ var matchesAccept = (file, accept) => {
13
+ const tokens = accept?.split(",").map((token) => token.trim().toLowerCase()).filter(Boolean) ?? [];
14
+ if (tokens.length === 0) return true;
15
+ const type = file.type.toLowerCase();
16
+ const name = file.name.toLowerCase();
17
+ return tokens.some((token) => {
18
+ if (token.startsWith(".")) return name.endsWith(token);
19
+ if (token.endsWith("/*")) return type.startsWith(token.slice(0, -1));
20
+ return type === token;
21
+ });
22
+ };
23
+ /**
24
+ * The files of a drop event that match `accept`. Non-file items (dragged text,
25
+ * directories) are skipped, and the filter runs on the drop item — so a file
26
+ * `accept` rejects is never read from disk.
27
+ *
28
+ * @internal
29
+ */
30
+ var getAcceptedFiles = async (items, accept) => await Promise.all(items.filter((item) => item.kind === "file").filter((item) => matchesAccept(item, accept)).map(async (item) => await item.getFile()));
31
+ //#endregion
32
+ export { getAcceptedFiles, matchesAccept };
33
+
34
+ //# sourceMappingURL=acceptedFiles.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acceptedFiles.mjs","names":[],"sources":["../../../../../../../src/lib/files/acceptedFiles.ts"],"sourcesContent":["import type { DropItem } from \"@react-types/shared\";\n\n/**\n * Whether a file matches an `accept` attribute value — a comma separated list\n * of MIME types (`image/png`), MIME wildcards (`image/*`) and file extensions\n * (`.png`). A missing or empty list matches everything, the way the attribute\n * itself does.\n *\n * @internal\n */\nexport const matchesAccept = (\n file: Pick<File, \"name\" | \"type\">,\n accept?: string,\n): boolean => {\n const tokens =\n accept\n ?.split(\",\")\n .map((token) => token.trim().toLowerCase())\n .filter(Boolean) ?? [];\n\n if (tokens.length === 0) {\n return true;\n }\n\n const type = file.type.toLowerCase();\n const name = file.name.toLowerCase();\n\n return tokens.some((token) => {\n if (token.startsWith(\".\")) {\n return name.endsWith(token);\n }\n if (token.endsWith(\"/*\")) {\n return type.startsWith(token.slice(0, -1));\n }\n return type === token;\n });\n};\n\n/**\n * The files of a drop event that match `accept`. Non-file items (dragged text,\n * directories) are skipped, and the filter runs on the drop item — so a file\n * `accept` rejects is never read from disk.\n *\n * @internal\n */\nexport const getAcceptedFiles = async (\n items: readonly DropItem[],\n accept?: string,\n): Promise<File[]> =>\n await Promise.all(\n items\n .filter((item) => item.kind === \"file\")\n .filter((item) => matchesAccept(item, accept))\n .map(async (item) => await item.getFile()),\n );\n"],"mappings":";;;;;;;;;;;AAUA,IAAa,iBACX,MACA,WACY;CACZ,MAAM,SACJ,QACI,MAAM,GAAG,CAAC,CACX,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAC1C,OAAO,OAAO,KAAK,CAAC;CAEzB,IAAI,OAAO,WAAW,GACpB,OAAO;CAGT,MAAM,OAAO,KAAK,KAAK,YAAY;CACnC,MAAM,OAAO,KAAK,KAAK,YAAY;CAEnC,OAAO,OAAO,MAAM,UAAU;EAC5B,IAAI,MAAM,WAAW,GAAG,GACtB,OAAO,KAAK,SAAS,KAAK;EAE5B,IAAI,MAAM,SAAS,IAAI,GACrB,OAAO,KAAK,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC;EAE3C,OAAO,SAAS;CAClB,CAAC;AACH;;;;;;;;AASA,IAAa,mBAAmB,OAC9B,OACA,WAEA,MAAM,QAAQ,IACZ,MACG,QAAQ,SAAS,KAAK,SAAS,MAAM,CAAC,CACtC,QAAQ,SAAS,cAAc,MAAM,MAAM,CAAC,CAAC,CAC7C,IAAI,OAAO,SAAS,MAAM,KAAK,QAAQ,CAAC,CAC7C"}
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/flow-react-components",
3
- "version": "1.3.0-next.14",
3
+ "version": "1.3.0-next.16",
4
4
  "type": "module",
5
5
  "description": "A React implementation of Flow, mittwald’s design system",
6
6
  "homepage": "https://flow.mittwald.de",
@@ -83,9 +83,9 @@
83
83
  "@internationalized/string-compiler": "^3.4.1",
84
84
  "@lezer/common": "^1.5.2",
85
85
  "@lezer/highlight": "^1.2.3",
86
- "@mittwald/flow-icons": "^1.3.0-next.14",
86
+ "@mittwald/flow-icons": "^1.3.0-next.16",
87
87
  "@mittwald/password-tools-js": "^3.0.0",
88
- "@mittwald/react-tunnel": "^1.3.0-next.14",
88
+ "@mittwald/react-tunnel": "^1.3.0-next.16",
89
89
  "@mittwald/react-use-promise": "^4.2.2",
90
90
  "@react-aria/form": "^3.2.1",
91
91
  "@react-aria/i18n": "^3.13.1",
@@ -138,12 +138,12 @@
138
138
  "@internationalized/date": "^3.12.3",
139
139
  "@lezer/generator": "^1.8.0",
140
140
  "@lezer/lr": "^1.4.10",
141
- "@mittwald/flow-core": "1.3.0-next.14",
142
- "@mittwald/flow-design-tokens": "1.3.0-next.14",
143
- "@mittwald/flow-icons-base": "1.3.0-next.14",
141
+ "@mittwald/flow-core": "1.3.0-next.16",
142
+ "@mittwald/flow-design-tokens": "1.3.0-next.16",
143
+ "@mittwald/flow-icons-base": "1.3.0-next.16",
144
144
  "@mittwald/react-use-promise": "^4.2.2",
145
145
  "@mittwald/remote-dom-react": "1.2.2-mittwald.10",
146
- "@mittwald/typescript-config": "1.3.0-next.14",
146
+ "@mittwald/typescript-config": "1.3.0-next.16",
147
147
  "@nx/storybook": "^22.7.6",
148
148
  "@storybook/addon-a11y": "^10.5.10",
149
149
  "@storybook/addon-actions": "^9.0.8",
@@ -195,7 +195,7 @@
195
195
  },
196
196
  "peerDependencies": {
197
197
  "@internationalized/date": "^3.12.2",
198
- "@mittwald/flow-icons-pro": "1.3.0-next.14",
198
+ "@mittwald/flow-icons-pro": "1.3.0-next.16",
199
199
  "@mittwald/react-use-promise": "^4.2.2",
200
200
  "next": "^16.2.3",
201
201
  "react": "^19.2.0",
@@ -219,5 +219,5 @@
219
219
  "browserslist": [
220
220
  "baseline widely available"
221
221
  ],
222
- "gitHead": "4fcbd2b4a9f5c2a6f96101731f1d83ed0b9681e0"
222
+ "gitHead": "cc707b77b1d0685331dbf073cb2376d78c2bcdf0"
223
223
  }