smarthr-ui 75.1.1 → 75.2.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.
Files changed (33) hide show
  1. package/esm/_virtual/index.js +4 -4
  2. package/esm/_virtual/index2.js +4 -4
  3. package/esm/_virtual/index3.js +4 -4
  4. package/esm/components/Combobox/MultiCombobox/MultiCombobox.js +1 -1
  5. package/esm/components/Combobox/SingleCombobox/SingleCombobox.js +1 -1
  6. package/esm/components/Combobox/helper.js +1 -1
  7. package/esm/components/Combobox/useOptions.js +1 -1
  8. package/esm/components/Dropdown/DropdownMenuButton/DropdownMenuButton.js +1 -1
  9. package/esm/components/Dropdown/FilterDropdown/FilterDropdown.js +1 -1
  10. package/esm/components/FileViewer/FileViewer.d.ts +1 -0
  11. package/esm/components/FileViewer/FileViewer.js +2 -2
  12. package/esm/components/FileViewer/FileViewer.js.map +1 -1
  13. package/esm/components/FileViewer/ImageViewer.js +2 -2
  14. package/esm/components/FileViewer/ImageViewer.js.map +1 -1
  15. package/esm/components/FileViewer/PDFViewer.js +2 -2
  16. package/esm/components/FileViewer/PDFViewer.js.map +1 -1
  17. package/esm/components/FileViewer/types.d.ts +1 -0
  18. package/esm/components/FormControl/FormControl.js +30 -1
  19. package/esm/components/FormControl/FormControl.js.map +1 -1
  20. package/esm/components/Tooltip/Tooltip.js +1 -1
  21. package/esm/libs/lodash.js +2 -2
  22. package/lib/components/FileViewer/FileViewer.d.ts +1 -0
  23. package/lib/components/FileViewer/FileViewer.js +2 -2
  24. package/lib/components/FileViewer/FileViewer.js.map +1 -1
  25. package/lib/components/FileViewer/ImageViewer.js +2 -2
  26. package/lib/components/FileViewer/ImageViewer.js.map +1 -1
  27. package/lib/components/FileViewer/PDFViewer.js +2 -2
  28. package/lib/components/FileViewer/PDFViewer.js.map +1 -1
  29. package/lib/components/FileViewer/types.d.ts +1 -0
  30. package/lib/components/FileViewer/types.js.map +1 -1
  31. package/lib/components/FormControl/FormControl.js +29 -0
  32. package/lib/components/FormControl/FormControl.js.map +1 -1
  33. package/package.json +1 -5
@@ -1,8 +1,8 @@
1
1
  import { getDefaultExportFromCjs } from './_commonjsHelpers.js';
2
- import { __require as requireReactInnertext } from './../vendor/.pnpm/react-innertext@1.1.5_@types_react@18.3.23_react@19.1.0/vendor/react-innertext/index.js';
2
+ import { __require as requireLodash_merge } from './../vendor/.pnpm/lodash.merge@4.6.2/vendor/lodash.merge/index.js';
3
3
 
4
- var reactInnertextExports = requireReactInnertext();
5
- var innerText = /*@__PURE__*/getDefaultExportFromCjs(reactInnertextExports);
4
+ var lodash_mergeExports = requireLodash_merge();
5
+ var _merge = /*@__PURE__*/getDefaultExportFromCjs(lodash_mergeExports);
6
6
 
7
- export { innerText as default };
7
+ export { _merge as default };
8
8
  //# sourceMappingURL=index.js.map
@@ -1,8 +1,8 @@
1
1
  import { getDefaultExportFromCjs } from './_commonjsHelpers.js';
2
- import { __require as requireLodash_merge } from './../vendor/.pnpm/lodash.merge@4.6.2/vendor/lodash.merge/index.js';
2
+ import { __require as requireLodash_range } from './../vendor/.pnpm/lodash.range@3.2.0/vendor/lodash.range/index.js';
3
3
 
4
- var lodash_mergeExports = requireLodash_merge();
5
- var _merge = /*@__PURE__*/getDefaultExportFromCjs(lodash_mergeExports);
4
+ var lodash_rangeExports = requireLodash_range();
5
+ var _range = /*@__PURE__*/getDefaultExportFromCjs(lodash_rangeExports);
6
6
 
7
- export { _merge as default };
7
+ export { _range as default };
8
8
  //# sourceMappingURL=index2.js.map
@@ -1,8 +1,8 @@
1
1
  import { getDefaultExportFromCjs } from './_commonjsHelpers.js';
2
- import { __require as requireLodash_range } from './../vendor/.pnpm/lodash.range@3.2.0/vendor/lodash.range/index.js';
2
+ import { __require as requireReactInnertext } from './../vendor/.pnpm/react-innertext@1.1.5_@types_react@18.3.23_react@19.1.0/vendor/react-innertext/index.js';
3
3
 
4
- var lodash_rangeExports = requireLodash_range();
5
- var _range = /*@__PURE__*/getDefaultExportFromCjs(lodash_rangeExports);
4
+ var reactInnertextExports = requireReactInnertext();
5
+ var innerText = /*@__PURE__*/getDefaultExportFromCjs(reactInnertextExports);
6
6
 
7
- export { _range as default };
7
+ export { innerText as default };
8
8
  //# sourceMappingURL=index3.js.map
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import { memo, useMemo, useRef, useState, useCallback, useImperativeHandle, useEffect, useId } from 'react';
4
- import innerText from '../../../_virtual/index.js';
4
+ import innerText from '../../../_virtual/index3.js';
5
5
  import { tv as ce } from './../../../vendor/.pnpm/tailwind-variants@0.3.1_tailwindcss@3.4.17_ts-node@10.9.2_@swc_core@1.12.11_@types_node@20.19.7_typescript@5.8.3__/vendor/tailwind-variants/dist/index.js';
6
6
  import { useOuterClick } from '../../../hooks/useOuterClick.js';
7
7
  import '../../../intl/IntlProvider.js';
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import { useRef, useState, useImperativeHandle, useCallback, useMemo, useEffect } from 'react';
4
- import innerText from '../../../_virtual/index.js';
4
+ import innerText from '../../../_virtual/index3.js';
5
5
  import { tv as ce } from './../../../vendor/.pnpm/tailwind-variants@0.3.1_tailwindcss@3.4.17_ts-node@10.9.2_@swc_core@1.12.11_@types_node@20.19.7_typescript@5.8.3__/vendor/tailwind-variants/dist/index.js';
6
6
  import { useClick } from '../../../hooks/useClick.js';
7
7
  import { useDecorators } from '../../../hooks/useDecorators.js';
@@ -1,4 +1,4 @@
1
- import innerText from '../../_virtual/index.js';
1
+ import innerText from '../../_virtual/index3.js';
2
2
 
3
3
  function convertMatchableString(original) {
4
4
  return (original
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useId, useMemo } from 'react';
2
- import innerText from '../../_virtual/index.js';
2
+ import innerText from '../../_virtual/index3.js';
3
3
  import { areItemsEqual, convertMatchableString } from './helper.js';
4
4
 
5
5
  const defaultIsItemSelected = (targetItem, selectedItems) => selectedItems.some((item) => areItemsEqual(item, targetItem));
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import { memo, useContext, useMemo, useRef, Children, isValidElement, Fragment, cloneElement } from 'react';
4
- import innerText from '../../../_virtual/index.js';
4
+ import innerText from '../../../_virtual/index3.js';
5
5
  import { tv as ce } from './../../../vendor/.pnpm/tailwind-variants@0.3.1_tailwindcss@3.4.17_ts-node@10.9.2_@swc_core@1.12.11_@types_node@20.19.7_typescript@5.8.3__/vendor/tailwind-variants/dist/index.js';
6
6
  import { DropdownContext, Dropdown } from '../Dropdown.js';
7
7
  import { DropdownTrigger } from '../DropdownTrigger.js';
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { useMemo } from 'react';
4
- import innerText from '../../../_virtual/index.js';
4
+ import innerText from '../../../_virtual/index3.js';
5
5
  import { tv as ce } from './../../../vendor/.pnpm/tailwind-variants@0.3.1_tailwindcss@3.4.17_ts-node@10.9.2_@swc_core@1.12.11_@types_node@20.19.7_typescript@5.8.3__/vendor/tailwind-variants/dist/index.js';
6
6
  import { useDecorators } from '../../../hooks/useDecorators.js';
7
7
  import { useResponseStatus } from '../../../hooks/useResponseStatus.js';
@@ -7,6 +7,7 @@ type Props = {
7
7
  scaleSteps?: number[];
8
8
  scaleStep?: number;
9
9
  onPassword?: ComponentProps<typeof PDFViewer>['onPassword'];
10
+ onLoadError?: () => void;
10
11
  };
11
12
  export declare const FileViewer: FC<Props>;
12
13
  export {};
@@ -31,7 +31,7 @@ import { Loader } from '../Loader/Loader.js';
31
31
 
32
32
  const defaultScaleStep = new Decimal(0.2);
33
33
  const defaultScaleSteps = [0.2, 0.6, 1, 1.6, 2, 3];
34
- const FileViewer = ({ file, scaleStep, scaleSteps, width: fixedWidth, onPassword, }) => {
34
+ const FileViewer = ({ file, scaleStep, scaleSteps, width: fixedWidth, onPassword, onLoadError, }) => {
35
35
  const ref = useRef(null);
36
36
  const [scale, setScale] = useState(1);
37
37
  const [loaded, setLoaded] = useState(false);
@@ -62,7 +62,7 @@ const FileViewer = ({ file, scaleStep, scaleSteps, width: fixedWidth, onPassword
62
62
  resizeObserver.disconnect();
63
63
  };
64
64
  }, [fixedWidth]);
65
- return (jsxs("div", { className: "shr-flex shr-h-full shr-w-full shr-flex-col shr-gap-2 shr-overflow-auto shr-bg-scrim shr-bg-[radial-gradient(theme(textColor.black)_1px,_transparent_0)] shr-bg-[length:16px_16px]", ref: ref, children: [jsx("div", { className: "shr-sticky shr-start-0 shr-top-0 shr-z-[1] shr-flex shr-w-full shr-flex-shrink-0 shr-gap-0.5", children: jsx(Controller, { scale: scale, setScale: setScale, scaleSteps: scaleSteps || defaultScaleSteps, onClickScaleUpButton: scaleUp, onClickScaleDownButton: scaleDown, onClickRotateButton: rotate }) }), jsxs("div", { className: "shr-z-[0] shr-mx-auto shr-my-0 shr-box-border shr-flex shr-w-fit shr-flex-shrink-0 shr-grow shr-items-center shr-justify-center shr-px-2 shr-pb-2", children: [!loaded && (jsx("div", { className: "shr-pointer-events-none shr-fixed shr-inset-0 shr-flex shr-h-full shr-w-full shr-items-center shr-justify-center", children: jsx(Loader, { type: "light", size: "m" }) })), jsx("div", { className: !loaded ? 'shr-invisible' : '', children: file.contentType === 'application/pdf' ? (jsx(PDFViewer, { scale: scale, rotation: rotation, file: file, width: width, onLoad: handleLoaded, onPassword: onPassword })) : file.contentType.startsWith('image/') ? (jsx(ImageViewer, { scale: scale, rotation: rotation, file: file, width: width, onLoad: handleLoaded })) : (jsx(Text, { children: jsx(Localizer, { id: "smarthr-ui/FileViewer/unsupportedFileText", defaultText: "\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u306A\u3044\u5F62\u5F0F\u306E\u30D5\u30A1\u30A4\u30EB\u3067\u3059\u3002" }) })) })] })] }));
65
+ return (jsxs("div", { className: "shr-flex shr-h-full shr-w-full shr-flex-col shr-gap-2 shr-overflow-auto shr-bg-scrim shr-bg-[radial-gradient(theme(textColor.black)_1px,_transparent_0)] shr-bg-[length:16px_16px]", ref: ref, children: [jsx("div", { className: "shr-sticky shr-start-0 shr-top-0 shr-z-[1] shr-flex shr-w-full shr-flex-shrink-0 shr-gap-0.5", children: jsx(Controller, { scale: scale, setScale: setScale, scaleSteps: scaleSteps || defaultScaleSteps, onClickScaleUpButton: scaleUp, onClickScaleDownButton: scaleDown, onClickRotateButton: rotate }) }), jsxs("div", { className: "shr-z-[0] shr-mx-auto shr-my-0 shr-box-border shr-flex shr-w-fit shr-flex-shrink-0 shr-grow shr-items-center shr-justify-center shr-px-2 shr-pb-2", children: [!loaded && (jsx("div", { className: "shr-pointer-events-none shr-fixed shr-inset-0 shr-flex shr-h-full shr-w-full shr-items-center shr-justify-center", children: jsx(Loader, { type: "light", size: "m" }) })), jsx("div", { className: !loaded ? 'shr-invisible' : '', children: file.contentType === 'application/pdf' ? (jsx(PDFViewer, { scale: scale, rotation: rotation, file: file, width: width, onLoad: handleLoaded, onPassword: onPassword, onLoadError: onLoadError })) : file.contentType.startsWith('image/') ? (jsx(ImageViewer, { scale: scale, rotation: rotation, file: file, width: width, onLoad: handleLoaded, onLoadError: onLoadError })) : (jsx(Text, { children: jsx(Localizer, { id: "smarthr-ui/FileViewer/unsupportedFileText", defaultText: "\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u306A\u3044\u5F62\u5F0F\u306E\u30D5\u30A1\u30A4\u30EB\u3067\u3059\u3002" }) })) })] })] }));
66
66
  };
67
67
  const Controller = memo(({ scale, setScale, scaleSteps, onClickScaleUpButton, onClickScaleDownButton, onClickRotateButton, }) => (jsx("div", { className: "shr-sticky shr-flex shr-w-full shr-items-center shr-justify-center shr-justify-items-center shr-gap-0.5 shr-bg-scrim shr-p-0.5 shr-shadow-layer-1", children: jsxs(Cluster, { gap: 0.5, children: [jsxs("div", { className: "shr-border-shorthand shr-flex shr-divide-x shr-divide-solid shr-overflow-hidden shr-rounded-m", children: [jsx(Button, { onClick: onClickScaleDownButton, disabled: scale <= scaleSteps[0], className: "shr-rounded-none shr-border-none", children: jsx(FaMagnifyingGlassMinusIcon, { alt: jsx(Localizer, { id: "smarthr-ui/FileViewer/scaleDownAlt", defaultText: "\u7E2E\u5C0F" }) }) }), jsx(DropdownMenuButton, { label: jsxs(Text, { children: [jsx(VisuallyHiddenText, { children: jsx(Localizer, { id: "smarthr-ui/FileViewer/scaleRateLabel", defaultText: "\u62E1\u5927\u7387" }) }), `${(scale * 100).toFixed(0)}%`] }), className: "shr-border-y-0 shr-border-[theme(borderColor.default)] [&_.smarthr-ui-Button]:shr-rounded-none [&_.smarthr-ui-Button]:shr-border-[transparent]", children: scaleSteps.map((step) => (jsx(Button, { onClick: () => setScale(step), className: "shr-rounded-none shr-border-0", children: `${(step * 100).toFixed(0)}%` }, step.toString()))) }), jsx(Button, { onClick: onClickScaleUpButton, className: "shr-rounded-none shr-border-0", children: jsx(FaMagnifyingGlassPlusIcon, { alt: jsx(Localizer, { id: "smarthr-ui/FileViewer/scaleUpAlt", defaultText: "\u62E1\u5927" }) }) })] }), jsx(Button, { onClick: onClickRotateButton, className: "shr-p-0.75", children: jsx(FaArrowRotateLeftIcon, { alt: jsx(Localizer, { id: "smarthr-ui/FileViewer/rotateAlt", defaultText: "\u5DE6\u56DE\u8EE2" }) }) })] }) })));
68
68
 
@@ -1 +1 @@
1
- {"version":3,"file":"FileViewer.js","sources":["../../../src/components/FileViewer/FileViewer.tsx"],"sourcesContent":["'use client'\n\nimport Decimal from 'decimal.js'\nimport {\n type ComponentProps,\n type FC,\n memo,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n Button,\n Cluster,\n DropdownMenuButton,\n FaArrowRotateLeftIcon,\n FaMagnifyingGlassMinusIcon,\n FaMagnifyingGlassPlusIcon,\n Loader,\n Text,\n VisuallyHiddenText,\n} from '../..'\nimport { Localizer } from '../../intl'\n\nimport { ImageViewer } from './ImageViewer'\nimport { PDFViewer } from './PDFViewer'\n\nimport type { FileForViewer } from './types'\n\nconst defaultScaleStep = new Decimal(0.2)\nconst defaultScaleSteps = [0.2, 0.6, 1, 1.6, 2, 3]\n\ntype Props = {\n file: FileForViewer\n width?: number\n\n /*\n * 拡大縮小率のステップを、100%を1とした配列で指定します。\n * */\n scaleSteps?: number[]\n\n scaleStep?: number\n onPassword?: ComponentProps<typeof PDFViewer>['onPassword']\n}\n\nexport const FileViewer: FC<Props> = ({\n file,\n scaleStep,\n scaleSteps,\n width: fixedWidth,\n onPassword,\n}) => {\n const ref = useRef<HTMLDivElement>(null)\n const [scale, setScale] = useState(1)\n const [loaded, setLoaded] = useState(false)\n const [rotation, setRotation] = useState(0)\n const [width, setWidth] = useState(fixedWidth ?? 0)\n\n const internalScaleStep = useMemo(\n () => (scaleStep ? new Decimal(scaleStep) : defaultScaleStep),\n [scaleStep],\n )\n\n const scaleUp = useCallback(() => {\n setScale((currentScale) => new Decimal(currentScale).add(internalScaleStep).toNumber())\n }, [internalScaleStep])\n\n const scaleDown = useCallback(() => {\n setScale((currentScale) => new Decimal(currentScale).sub(internalScaleStep).toNumber())\n }, [internalScaleStep])\n\n const rotate = useCallback(() => {\n setRotation((currentRotation) => currentRotation - 90)\n }, [])\n\n const handleLoaded = useCallback(() => {\n setLoaded(true)\n }, [])\n\n useEffect(() => {\n if (!ref.current || fixedWidth !== undefined) {\n return\n }\n\n const resizeObserver = new ResizeObserver(() => {\n setWidth((ref.current?.clientWidth ?? 0) - 64)\n })\n\n resizeObserver.observe(ref.current)\n\n return () => {\n resizeObserver.disconnect()\n }\n }, [fixedWidth])\n\n return (\n <div\n className=\"shr-flex shr-h-full shr-w-full shr-flex-col shr-gap-2 shr-overflow-auto shr-bg-scrim shr-bg-[radial-gradient(theme(textColor.black)_1px,_transparent_0)] shr-bg-[length:16px_16px]\"\n ref={ref}\n >\n <div className=\"shr-sticky shr-start-0 shr-top-0 shr-z-[1] shr-flex shr-w-full shr-flex-shrink-0 shr-gap-0.5\">\n <Controller\n scale={scale}\n setScale={setScale}\n scaleSteps={scaleSteps || defaultScaleSteps}\n onClickScaleUpButton={scaleUp}\n onClickScaleDownButton={scaleDown}\n onClickRotateButton={rotate}\n />\n </div>\n <div className=\"shr-z-[0] shr-mx-auto shr-my-0 shr-box-border shr-flex shr-w-fit shr-flex-shrink-0 shr-grow shr-items-center shr-justify-center shr-px-2 shr-pb-2\">\n {!loaded && (\n <div className=\"shr-pointer-events-none shr-fixed shr-inset-0 shr-flex shr-h-full shr-w-full shr-items-center shr-justify-center\">\n <Loader type=\"light\" size=\"m\" />\n </div>\n )}\n <div className={!loaded ? 'shr-invisible' : ''}>\n {file.contentType === 'application/pdf' ? (\n <PDFViewer\n scale={scale}\n rotation={rotation}\n file={file}\n width={width}\n onLoad={handleLoaded}\n onPassword={onPassword}\n />\n ) : file.contentType.startsWith('image/') ? (\n <ImageViewer\n scale={scale}\n rotation={rotation}\n file={file}\n width={width}\n onLoad={handleLoaded}\n />\n ) : (\n <Text>\n <Localizer\n id=\"smarthr-ui/FileViewer/unsupportedFileText\"\n defaultText=\"サポートされていない形式のファイルです。\"\n />\n </Text>\n )}\n </div>\n </div>\n </div>\n )\n}\n\ntype ControllerProps = {\n scale: number\n setScale: (scale: number) => void\n scaleSteps: number[]\n onClickScaleUpButton: () => void\n onClickScaleDownButton: () => void\n onClickRotateButton: () => void\n}\n\nconst Controller: FC<ControllerProps> = memo(\n ({\n scale,\n setScale,\n scaleSteps,\n onClickScaleUpButton,\n onClickScaleDownButton,\n onClickRotateButton,\n }) => (\n <div className=\"shr-sticky shr-flex shr-w-full shr-items-center shr-justify-center shr-justify-items-center shr-gap-0.5 shr-bg-scrim shr-p-0.5 shr-shadow-layer-1\">\n <Cluster gap={0.5}>\n <div className=\"shr-border-shorthand shr-flex shr-divide-x shr-divide-solid shr-overflow-hidden shr-rounded-m\">\n <Button\n onClick={onClickScaleDownButton}\n disabled={scale <= scaleSteps[0]}\n className=\"shr-rounded-none shr-border-none\"\n >\n <FaMagnifyingGlassMinusIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/scaleDownAlt\" defaultText=\"縮小\" />}\n />\n </Button>\n <DropdownMenuButton\n label={\n <Text>\n <VisuallyHiddenText>\n <Localizer id=\"smarthr-ui/FileViewer/scaleRateLabel\" defaultText=\"拡大率\" />\n </VisuallyHiddenText>\n {`${(scale * 100).toFixed(0)}%`}\n </Text>\n }\n className=\"shr-border-y-0 shr-border-[theme(borderColor.default)] [&_.smarthr-ui-Button]:shr-rounded-none [&_.smarthr-ui-Button]:shr-border-[transparent]\"\n >\n {scaleSteps.map((step) => (\n <Button\n key={step.toString()}\n onClick={() => setScale(step)}\n className=\"shr-rounded-none shr-border-0\"\n >\n {`${(step * 100).toFixed(0)}%`}\n </Button>\n ))}\n </DropdownMenuButton>\n <Button onClick={onClickScaleUpButton} className=\"shr-rounded-none shr-border-0\">\n <FaMagnifyingGlassPlusIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/scaleUpAlt\" defaultText=\"拡大\" />}\n />\n </Button>\n </div>\n <Button onClick={onClickRotateButton} className=\"shr-p-0.75\">\n <FaArrowRotateLeftIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/rotateAlt\" defaultText=\"左回転\" />}\n />\n </Button>\n </Cluster>\n </div>\n ),\n)\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA;AACA;AAeO;AAOL;;;;AAIA;;AAOA;;AAEA;AAEA;;AAEA;AAEA;;;AAIA;;;;;;;AASE;AACE;AACF;AAEA;AAEA;;AAEA;AACF;;AAqDF;AAWA;;"}
1
+ {"version":3,"file":"FileViewer.js","sources":["../../../src/components/FileViewer/FileViewer.tsx"],"sourcesContent":["'use client'\n\nimport Decimal from 'decimal.js'\nimport {\n type ComponentProps,\n type FC,\n memo,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n Button,\n Cluster,\n DropdownMenuButton,\n FaArrowRotateLeftIcon,\n FaMagnifyingGlassMinusIcon,\n FaMagnifyingGlassPlusIcon,\n Loader,\n Text,\n VisuallyHiddenText,\n} from '../..'\nimport { Localizer } from '../../intl'\n\nimport { ImageViewer } from './ImageViewer'\nimport { PDFViewer } from './PDFViewer'\n\nimport type { FileForViewer } from './types'\n\nconst defaultScaleStep = new Decimal(0.2)\nconst defaultScaleSteps = [0.2, 0.6, 1, 1.6, 2, 3]\n\ntype Props = {\n file: FileForViewer\n width?: number\n\n /*\n * 拡大縮小率のステップを、100%を1とした配列で指定します。\n * */\n scaleSteps?: number[]\n\n scaleStep?: number\n onPassword?: ComponentProps<typeof PDFViewer>['onPassword']\n onLoadError?: () => void\n}\n\nexport const FileViewer: FC<Props> = ({\n file,\n scaleStep,\n scaleSteps,\n width: fixedWidth,\n onPassword,\n onLoadError,\n}) => {\n const ref = useRef<HTMLDivElement>(null)\n const [scale, setScale] = useState(1)\n const [loaded, setLoaded] = useState(false)\n const [rotation, setRotation] = useState(0)\n const [width, setWidth] = useState(fixedWidth ?? 0)\n\n const internalScaleStep = useMemo(\n () => (scaleStep ? new Decimal(scaleStep) : defaultScaleStep),\n [scaleStep],\n )\n\n const scaleUp = useCallback(() => {\n setScale((currentScale) => new Decimal(currentScale).add(internalScaleStep).toNumber())\n }, [internalScaleStep])\n\n const scaleDown = useCallback(() => {\n setScale((currentScale) => new Decimal(currentScale).sub(internalScaleStep).toNumber())\n }, [internalScaleStep])\n\n const rotate = useCallback(() => {\n setRotation((currentRotation) => currentRotation - 90)\n }, [])\n\n const handleLoaded = useCallback(() => {\n setLoaded(true)\n }, [])\n\n useEffect(() => {\n if (!ref.current || fixedWidth !== undefined) {\n return\n }\n\n const resizeObserver = new ResizeObserver(() => {\n setWidth((ref.current?.clientWidth ?? 0) - 64)\n })\n\n resizeObserver.observe(ref.current)\n\n return () => {\n resizeObserver.disconnect()\n }\n }, [fixedWidth])\n\n return (\n <div\n className=\"shr-flex shr-h-full shr-w-full shr-flex-col shr-gap-2 shr-overflow-auto shr-bg-scrim shr-bg-[radial-gradient(theme(textColor.black)_1px,_transparent_0)] shr-bg-[length:16px_16px]\"\n ref={ref}\n >\n <div className=\"shr-sticky shr-start-0 shr-top-0 shr-z-[1] shr-flex shr-w-full shr-flex-shrink-0 shr-gap-0.5\">\n <Controller\n scale={scale}\n setScale={setScale}\n scaleSteps={scaleSteps || defaultScaleSteps}\n onClickScaleUpButton={scaleUp}\n onClickScaleDownButton={scaleDown}\n onClickRotateButton={rotate}\n />\n </div>\n <div className=\"shr-z-[0] shr-mx-auto shr-my-0 shr-box-border shr-flex shr-w-fit shr-flex-shrink-0 shr-grow shr-items-center shr-justify-center shr-px-2 shr-pb-2\">\n {!loaded && (\n <div className=\"shr-pointer-events-none shr-fixed shr-inset-0 shr-flex shr-h-full shr-w-full shr-items-center shr-justify-center\">\n <Loader type=\"light\" size=\"m\" />\n </div>\n )}\n <div className={!loaded ? 'shr-invisible' : ''}>\n {file.contentType === 'application/pdf' ? (\n <PDFViewer\n scale={scale}\n rotation={rotation}\n file={file}\n width={width}\n onLoad={handleLoaded}\n onPassword={onPassword}\n onLoadError={onLoadError}\n />\n ) : file.contentType.startsWith('image/') ? (\n <ImageViewer\n scale={scale}\n rotation={rotation}\n file={file}\n width={width}\n onLoad={handleLoaded}\n onLoadError={onLoadError}\n />\n ) : (\n <Text>\n <Localizer\n id=\"smarthr-ui/FileViewer/unsupportedFileText\"\n defaultText=\"サポートされていない形式のファイルです。\"\n />\n </Text>\n )}\n </div>\n </div>\n </div>\n )\n}\n\ntype ControllerProps = {\n scale: number\n setScale: (scale: number) => void\n scaleSteps: number[]\n onClickScaleUpButton: () => void\n onClickScaleDownButton: () => void\n onClickRotateButton: () => void\n}\n\nconst Controller: FC<ControllerProps> = memo(\n ({\n scale,\n setScale,\n scaleSteps,\n onClickScaleUpButton,\n onClickScaleDownButton,\n onClickRotateButton,\n }) => (\n <div className=\"shr-sticky shr-flex shr-w-full shr-items-center shr-justify-center shr-justify-items-center shr-gap-0.5 shr-bg-scrim shr-p-0.5 shr-shadow-layer-1\">\n <Cluster gap={0.5}>\n <div className=\"shr-border-shorthand shr-flex shr-divide-x shr-divide-solid shr-overflow-hidden shr-rounded-m\">\n <Button\n onClick={onClickScaleDownButton}\n disabled={scale <= scaleSteps[0]}\n className=\"shr-rounded-none shr-border-none\"\n >\n <FaMagnifyingGlassMinusIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/scaleDownAlt\" defaultText=\"縮小\" />}\n />\n </Button>\n <DropdownMenuButton\n label={\n <Text>\n <VisuallyHiddenText>\n <Localizer id=\"smarthr-ui/FileViewer/scaleRateLabel\" defaultText=\"拡大率\" />\n </VisuallyHiddenText>\n {`${(scale * 100).toFixed(0)}%`}\n </Text>\n }\n className=\"shr-border-y-0 shr-border-[theme(borderColor.default)] [&_.smarthr-ui-Button]:shr-rounded-none [&_.smarthr-ui-Button]:shr-border-[transparent]\"\n >\n {scaleSteps.map((step) => (\n <Button\n key={step.toString()}\n onClick={() => setScale(step)}\n className=\"shr-rounded-none shr-border-0\"\n >\n {`${(step * 100).toFixed(0)}%`}\n </Button>\n ))}\n </DropdownMenuButton>\n <Button onClick={onClickScaleUpButton} className=\"shr-rounded-none shr-border-0\">\n <FaMagnifyingGlassPlusIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/scaleUpAlt\" defaultText=\"拡大\" />}\n />\n </Button>\n </div>\n <Button onClick={onClickRotateButton} className=\"shr-p-0.75\">\n <FaArrowRotateLeftIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/rotateAlt\" defaultText=\"左回転\" />}\n />\n </Button>\n </Cluster>\n </div>\n ),\n)\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA;AACA;;AAwBE;;;;AAIA;;AAOA;;AAEA;AAEA;;AAEA;AAEA;;;AAIA;;;;;;;AASE;AACE;AACF;AAEA;AAEA;;AAEA;AACF;;AAuDF;AAWA;;"}
@@ -2,7 +2,7 @@
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { memo, useRef, useState, useCallback, useEffect } from 'react';
4
4
 
5
- const ImageViewer = memo(({ scale, rotation, file, width, onLoad }) => {
5
+ const ImageViewer = memo(({ scale, rotation, file, width, onLoad, onLoadError }) => {
6
6
  const imageRef = useRef(null);
7
7
  const [viewConfig, setViewConfig] = useState({
8
8
  wrapperWidth: 0,
@@ -42,7 +42,7 @@ const ImageViewer = memo(({ scale, rotation, file, width, onLoad }) => {
42
42
  }, className: "shr-relative shr-h-full shr-w-full", children: jsx("img", { className: "shr-absolute shr-left-[50%] shr-top-[50%] shr-origin-top-left -shr-translate-x-1/2 -shr-translate-y-1/2", ref: imageRef, src: file.url, alt: file.alt, style: {
43
43
  rotate: `${rotation}deg`,
44
44
  scale: `${viewConfig.imgScale}`,
45
- }, onLoad: handleLoad }) }));
45
+ }, onLoad: handleLoad, onError: onLoadError }) }));
46
46
  });
47
47
 
48
48
  export { ImageViewer };
@@ -1 +1 @@
1
- {"version":3,"file":"ImageViewer.js","sources":["../../../src/components/FileViewer/ImageViewer.tsx"],"sourcesContent":["'use client'\n\nimport { type FC, memo, useCallback, useEffect, useRef, useState } from 'react'\n\nimport type { ViewerProps } from './types'\n\nexport const ImageViewer: FC<ViewerProps> = memo(({ scale, rotation, file, width, onLoad }) => {\n const imageRef = useRef<HTMLImageElement>(null)\n const [viewConfig, setViewConfig] = useState({\n wrapperWidth: 0,\n wrapperHeight: 0,\n imgScale: 1,\n })\n\n // CSSのみではscale, transformの値を親に適用してスクロールするようにできないため、計算している\n const updateViewConfig = useCallback(() => {\n if (!imageRef.current?.complete) {\n return\n }\n\n const img = imageRef.current\n // 与えられたwidthに対する適切なscaleを算出\n const viewportScale = (width / img.naturalWidth) * scale\n\n const rad = (rotation * Math.PI) / 180\n const sin = Math.abs(Math.sin(rad))\n const cos = Math.abs(Math.cos(rad))\n\n // imgをwidth: 100%で表示したときと同等の値を算出\n const scaledWidth = img.naturalWidth * viewportScale\n const scaledHeight = img.naturalHeight * viewportScale\n\n setViewConfig({\n wrapperWidth: scaledWidth * cos + scaledHeight * sin,\n wrapperHeight: scaledWidth * sin + scaledHeight * cos,\n imgScale: viewportScale,\n })\n }, [scale, rotation, width])\n\n const handleLoad = useCallback(() => {\n updateViewConfig()\n onLoad?.()\n }, [updateViewConfig, onLoad])\n\n useEffect(() => {\n updateViewConfig()\n }, [updateViewConfig])\n\n return (\n <div\n style={{\n width: viewConfig.wrapperWidth,\n height: viewConfig.wrapperHeight,\n }}\n className=\"shr-relative shr-h-full shr-w-full\"\n >\n {/* imgのload完了時にupdateViewConfigを呼び出さないと適切なサイズが取得できないため */}\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}\n <img\n className=\"shr-absolute shr-left-[50%] shr-top-[50%] shr-origin-top-left -shr-translate-x-1/2 -shr-translate-y-1/2\"\n ref={imageRef}\n src={file.url}\n alt={file.alt}\n style={{\n rotate: `${rotation}deg`,\n scale: `${viewConfig.imgScale}`,\n }}\n onLoad={handleLoad}\n />\n </div>\n )\n})\n"],"names":[],"mappings":";;;;AAMO;AACL;AACA;AACE;AACA;AACA;AACD;;AAGD;AACE;;;AAIA;;;;AAKA;AACA;;AAGA;AACA;AAEA;AACE;AACA;AACA;AACD;;AAGH;AACE;;AAEF;;AAGE;AACF;;;;;;AAmBQ;AACD;AAKT;;"}
1
+ {"version":3,"file":"ImageViewer.js","sources":["../../../src/components/FileViewer/ImageViewer.tsx"],"sourcesContent":["'use client'\n\nimport { type FC, memo, useCallback, useEffect, useRef, useState } from 'react'\n\nimport type { ViewerProps } from './types'\n\nexport const ImageViewer: FC<ViewerProps> = memo(\n ({ scale, rotation, file, width, onLoad, onLoadError }) => {\n const imageRef = useRef<HTMLImageElement>(null)\n const [viewConfig, setViewConfig] = useState({\n wrapperWidth: 0,\n wrapperHeight: 0,\n imgScale: 1,\n })\n\n // CSSのみではscale, transformの値を親に適用してスクロールするようにできないため、計算している\n const updateViewConfig = useCallback(() => {\n if (!imageRef.current?.complete) {\n return\n }\n\n const img = imageRef.current\n // 与えられたwidthに対する適切なscaleを算出\n const viewportScale = (width / img.naturalWidth) * scale\n\n const rad = (rotation * Math.PI) / 180\n const sin = Math.abs(Math.sin(rad))\n const cos = Math.abs(Math.cos(rad))\n\n // imgをwidth: 100%で表示したときと同等の値を算出\n const scaledWidth = img.naturalWidth * viewportScale\n const scaledHeight = img.naturalHeight * viewportScale\n\n setViewConfig({\n wrapperWidth: scaledWidth * cos + scaledHeight * sin,\n wrapperHeight: scaledWidth * sin + scaledHeight * cos,\n imgScale: viewportScale,\n })\n }, [scale, rotation, width])\n\n const handleLoad = useCallback(() => {\n updateViewConfig()\n onLoad?.()\n }, [updateViewConfig, onLoad])\n\n useEffect(() => {\n updateViewConfig()\n }, [updateViewConfig])\n\n return (\n <div\n style={{\n width: viewConfig.wrapperWidth,\n height: viewConfig.wrapperHeight,\n }}\n className=\"shr-relative shr-h-full shr-w-full\"\n >\n {/* imgのload完了時にupdateViewConfigを呼び出さないと適切なサイズが取得できないため */}\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}\n <img\n className=\"shr-absolute shr-left-[50%] shr-top-[50%] shr-origin-top-left -shr-translate-x-1/2 -shr-translate-y-1/2\"\n ref={imageRef}\n src={file.url}\n alt={file.alt}\n style={{\n rotate: `${rotation}deg`,\n scale: `${viewConfig.imgScale}`,\n }}\n onLoad={handleLoad}\n onError={onLoadError}\n />\n </div>\n )\n },\n)\n"],"names":[],"mappings":";;;;;AAQI;AACA;AACE;AACA;AACA;AACD;;AAGD;AACE;;;AAIA;;;;AAKA;AACA;;AAGA;AACA;AAEA;AACE;AACA;AACA;AACD;;AAGH;AACE;;AAEF;;AAGE;AACF;;;;;;AAmBQ;;AAOV;;"}
@@ -39,7 +39,7 @@ const options = {
39
39
  // cMapUrl: '/cmaps/',
40
40
  cMapUrl: `//unpkg.com/pdfjs-dist@${__webpack_exports__version}/cmaps/`,
41
41
  };
42
- const PDFViewer = memo(({ scale, rotation, file, width, onLoad, onPassword }) => {
42
+ const PDFViewer = memo(({ scale, rotation, file, width, onLoad, onPassword, onLoadError }) => {
43
43
  const [pdfNumPages, setPdfNumPages] = useState(1);
44
44
  const onDocumentLoadSuccess = useCallback(({ numPages }) => {
45
45
  setPdfNumPages(numPages);
@@ -56,7 +56,7 @@ const PDFViewer = memo(({ scale, rotation, file, width, onLoad, onPassword }) =>
56
56
  onLoad();
57
57
  };
58
58
  }, [pdfNumPages, onLoad]);
59
- return (jsxs(Fragment, { children: [jsx(ReactPDFStyle, {}), jsx(Document, { options: options, file: file.url, onLoadSuccess: onDocumentLoadSuccess, rotate: rotation, className: "shr-flex shr-h-full shr-w-fit shr-flex-col shr-items-center shr-gap-1 shr-overflow-auto", externalLinkTarget: "_blank", loading: null, onPassword: onPassword, children: Array.from({ length: pdfNumPages }).map((_, i) => (jsx(Page, { pageNumber: i + 1, width: width, scale: scale, className: "shr-w-full", onLoadSuccess: onPageLoad, loading: null }, `page_${i}`))) })] }));
59
+ return (jsxs(Fragment, { children: [jsx(ReactPDFStyle, {}), jsx(Document, { options: options, file: file.url, onLoadSuccess: onDocumentLoadSuccess, onLoadError: onLoadError, rotate: rotation, className: "shr-flex shr-h-full shr-w-fit shr-flex-col shr-items-center shr-gap-1 shr-overflow-auto", externalLinkTarget: "_blank", loading: null, onPassword: onPassword, children: Array.from({ length: pdfNumPages }).map((_, i) => (jsx(Page, { pageNumber: i + 1, width: width, scale: scale, className: "shr-w-full", onLoadSuccess: onPageLoad, loading: null }, `page_${i}`))) })] }));
60
60
  });
61
61
 
62
62
  export { PDFViewer };
@@ -1 +1 @@
1
- {"version":3,"file":"PDFViewer.js","sources":["../../../src/components/FileViewer/PDFViewer.tsx"],"sourcesContent":["'use client'\n\nimport { type ComponentProps, type FC, memo, useCallback, useMemo, useState } from 'react'\nimport { Document, Page, pdfjs } from 'react-pdf'\n\nimport { ReactPDFStyle } from './generatedReactPDFStyle'\n\nimport type { ViewerProps } from './types'\n\nif (typeof window !== 'undefined') {\n // iOS 17.3以下ではPromise.withResolversが未定義のため、polyfillを適用する\n // @ts-expect-error\n if (typeof window.Promise.withResolvers === 'undefined') {\n // @ts-expect-error\n window.Promise.withResolvers = function () {\n let resolve, reject\n const promise = new Promise((res, rej) => {\n resolve = res\n reject = rej\n })\n return { promise, resolve, reject }\n }\n // web workerもpolyfillされたものを読み込む\n pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`\n } else {\n // TODO: バンドラの関係でCDNから読み込んでいるが、smarthr-uiから配信するようにしたい\n // pdfjs.GlobalWorkerOptions.workerSrc = new URL(\n // 'pdfjs-dist/build/pdf.worker.min.mjs',\n // import.meta.url,\n // ).toString()\n pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`\n }\n}\n\nconst options = {\n // TODO: バンドラの関係でCDNから読み込んでいるが、smarthr-uiから配信するようにしたい\n // 非latin文字を読み込むためのオプション\n // 参考: https://github.com/wojtekmaj/react-pdf?tab=readme-ov-file#support-for-non-latin-characters\n // cMapUrl: '/cmaps/',\n cMapUrl: `//unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/`,\n} satisfies ComponentProps<typeof Document>['options']\n\nexport const PDFViewer: FC<ViewerProps> = memo(\n ({ scale, rotation, file, width, onLoad, onPassword }) => {\n const [pdfNumPages, setPdfNumPages] = useState(1)\n\n const onDocumentLoadSuccess = useCallback<\n NonNullable<ComponentProps<typeof Document>['onLoadSuccess']>\n >(({ numPages }) => {\n setPdfNumPages(numPages)\n }, [])\n\n const onPageLoad: ComponentProps<typeof Page>['onLoadSuccess'] = useMemo(() => {\n if (!onLoad) {\n return undefined\n }\n\n return (page) => {\n // DocumentのLoadだとページごとの読み込みが考慮されないため\n if (page.pageNumber !== pdfNumPages) {\n return\n }\n onLoad()\n }\n }, [pdfNumPages, onLoad])\n\n return (\n <>\n {/* TODO: 外部CSSをsmarthr-uiから読み込んでもらえるようにする機構ができたら消す */}\n <ReactPDFStyle />\n <Document\n options={options}\n file={file.url}\n onLoadSuccess={onDocumentLoadSuccess}\n rotate={rotation}\n className=\"shr-flex shr-h-full shr-w-fit shr-flex-col shr-items-center shr-gap-1 shr-overflow-auto\"\n externalLinkTarget=\"_blank\"\n loading={null}\n onPassword={onPassword}\n >\n {Array.from({ length: pdfNumPages }).map((_, i) => (\n <Page\n key={`page_${i}`}\n pageNumber={i + 1}\n width={width}\n scale={scale}\n className=\"shr-w-full\"\n onLoadSuccess={onPageLoad}\n loading={null}\n />\n ))}\n </Document>\n </>\n )\n },\n)\n"],"names":[],"mappings":";;;;;;;;;AASA;;;;;AAKI;;;;;AAKE;AACA;AACF;;;;;;;;;;;;AAWJ;AAEA;;;;;AAKE;;;;;;;AAaE;;AAEI;;;;AAKA;;;AAGA;AACF;AACF;;AA8BF;;"}
1
+ {"version":3,"file":"PDFViewer.js","sources":["../../../src/components/FileViewer/PDFViewer.tsx"],"sourcesContent":["'use client'\n\nimport { type ComponentProps, type FC, memo, useCallback, useMemo, useState } from 'react'\nimport { Document, Page, pdfjs } from 'react-pdf'\n\nimport { ReactPDFStyle } from './generatedReactPDFStyle'\n\nimport type { ViewerProps } from './types'\n\nif (typeof window !== 'undefined') {\n // iOS 17.3以下ではPromise.withResolversが未定義のため、polyfillを適用する\n // @ts-expect-error\n if (typeof window.Promise.withResolvers === 'undefined') {\n // @ts-expect-error\n window.Promise.withResolvers = function () {\n let resolve, reject\n const promise = new Promise((res, rej) => {\n resolve = res\n reject = rej\n })\n return { promise, resolve, reject }\n }\n // web workerもpolyfillされたものを読み込む\n pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`\n } else {\n // TODO: バンドラの関係でCDNから読み込んでいるが、smarthr-uiから配信するようにしたい\n // pdfjs.GlobalWorkerOptions.workerSrc = new URL(\n // 'pdfjs-dist/build/pdf.worker.min.mjs',\n // import.meta.url,\n // ).toString()\n pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`\n }\n}\n\nconst options = {\n // TODO: バンドラの関係でCDNから読み込んでいるが、smarthr-uiから配信するようにしたい\n // 非latin文字を読み込むためのオプション\n // 参考: https://github.com/wojtekmaj/react-pdf?tab=readme-ov-file#support-for-non-latin-characters\n // cMapUrl: '/cmaps/',\n cMapUrl: `//unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/`,\n} satisfies ComponentProps<typeof Document>['options']\n\nexport const PDFViewer: FC<ViewerProps> = memo(\n ({ scale, rotation, file, width, onLoad, onPassword, onLoadError }) => {\n const [pdfNumPages, setPdfNumPages] = useState(1)\n\n const onDocumentLoadSuccess = useCallback<\n NonNullable<ComponentProps<typeof Document>['onLoadSuccess']>\n >(({ numPages }) => {\n setPdfNumPages(numPages)\n }, [])\n\n const onPageLoad: ComponentProps<typeof Page>['onLoadSuccess'] = useMemo(() => {\n if (!onLoad) {\n return undefined\n }\n\n return (page) => {\n // DocumentのLoadだとページごとの読み込みが考慮されないため\n if (page.pageNumber !== pdfNumPages) {\n return\n }\n onLoad()\n }\n }, [pdfNumPages, onLoad])\n\n return (\n <>\n {/* TODO: 外部CSSをsmarthr-uiから読み込んでもらえるようにする機構ができたら消す */}\n <ReactPDFStyle />\n <Document\n options={options}\n file={file.url}\n onLoadSuccess={onDocumentLoadSuccess}\n onLoadError={onLoadError}\n rotate={rotation}\n className=\"shr-flex shr-h-full shr-w-fit shr-flex-col shr-items-center shr-gap-1 shr-overflow-auto\"\n externalLinkTarget=\"_blank\"\n loading={null}\n onPassword={onPassword}\n >\n {Array.from({ length: pdfNumPages }).map((_, i) => (\n <Page\n key={`page_${i}`}\n pageNumber={i + 1}\n width={width}\n scale={scale}\n className=\"shr-w-full\"\n onLoadSuccess={onPageLoad}\n loading={null}\n />\n ))}\n </Document>\n </>\n )\n },\n)\n"],"names":[],"mappings":";;;;;;;;;AASA;;;;;AAKI;;;;;AAKE;AACA;AACF;;;;;;;;;;;;AAWJ;AAEA;;;;;AAKE;;;;;;;AAaE;;AAEI;;;;AAKA;;;AAGA;AACF;AACF;;AA+BF;;"}
@@ -15,4 +15,5 @@ export type ViewerProps = {
15
15
  * PDFファイルのパスワード入力を要求されたときに呼ばれるコールバック関数。PdfViewerでのみ使用されます。
16
16
  */
17
17
  onPassword?: ComponentProps<typeof Document>['onPassword'];
18
+ onLoadError?: () => void;
18
19
  };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { memo, useMemo, useId, useRef, Fragment as Fragment$1, createElement, useEffect } from 'react';
3
3
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
4
- import innerText from '../../_virtual/index.js';
4
+ import innerText from '../../_virtual/index3.js';
5
5
  import { tv as ce } from './../../vendor/.pnpm/tailwind-variants@0.3.1_tailwindcss@3.4.17_ts-node@10.9.2_@swc_core@1.12.11_@types_node@20.19.7_typescript@5.8.3__/vendor/tailwind-variants/dist/index.js';
6
6
  import { FaCircleExclamationIcon } from '../Icon/Icon.js';
7
7
  import '../Icon/generateIcon.js';
@@ -192,6 +192,35 @@ const ActualFormControl = ({ title, titleType = 'blockTitle', subActionArea, dan
192
192
  }
193
193
  }
194
194
  }, [actualErrorMessages.length, autoBindErrorInput]);
195
+ // HINT: Fieldset内の可視ラベルが無いinputに、legend文言をアクセシブルネームに追加する
196
+ // https://waic.jp/translations/WCAG21/Understanding/label-in-name.html
197
+ useEffect(() => {
198
+ if (!isFieldset || !inputWrapperRef.current)
199
+ return;
200
+ const inputs = inputWrapperRef.current.querySelectorAll(SMARTHR_UI_INPUT_SELECTOR);
201
+ if (!inputs.length)
202
+ return;
203
+ const legendText = innerText(title);
204
+ if (!legendText)
205
+ return;
206
+ const labels = new Set(Array.from(document.querySelectorAll('label[for]')).map((label) => label.getAttribute('for')));
207
+ inputs.forEach((input) => {
208
+ const inputId = input.getAttribute('id');
209
+ if (inputId && labels.has(inputId)) {
210
+ return;
211
+ }
212
+ let accessibleName = '';
213
+ if (input.hasAttribute('aria-label')) {
214
+ accessibleName = input.getAttribute('aria-label') || '';
215
+ }
216
+ else if (input.hasAttribute('title')) {
217
+ accessibleName = input.getAttribute('title') || '';
218
+ }
219
+ if (!accessibleName.includes(legendText)) {
220
+ input.setAttribute('aria-label', `${legendText} ${accessibleName}`.trim());
221
+ }
222
+ });
223
+ }, [isFieldset, title]);
195
224
  let body = (jsxs(Fragment, { children: [jsx(HelpMessageParagraph, { helpMessage: helpMessage, managedHtmlFor: managedHtmlFor }), jsx(ExampleMessageText, { exampleMessage: exampleMessage, managedHtmlFor: managedHtmlFor }), jsx(ErrorMessageList, { errorMessages: actualErrorMessages, managedHtmlFor: managedHtmlFor, classNames: classNames }), jsx("div", { className: classNames.childrenWrapper, ref: inputWrapperRef, children: children }), jsx(SupplementaryMessageText, { supplementaryMessage: supplementaryMessage, managedHtmlFor: managedHtmlFor })] }));
196
225
  // HINT: dangerouslyTitleHiddenの場合、body以下の余白の計算を簡略化するため
197
226
  // Stackをネストし、そのStackに対してmargin-top: 0を指定する
@@ -1 +1 @@
1
- {"version":3,"file":"FormControl.js","sources":["../../../src/components/FormControl/FormControl.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type ComponentType,\n type FC,\n Fragment,\n type FunctionComponentElement,\n type PropsWithChildren,\n type ReactNode,\n memo,\n useEffect,\n useMemo,\n useRef,\n} from 'react'\nimport { useId } from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { FaCircleExclamationIcon } from '../Icon'\nimport { Cluster, Stack } from '../Layout'\nimport { StatusLabel } from '../StatusLabel'\nimport { Text, type TextProps } from '../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassNameGenerator } from '../VisuallyHiddenText'\n\nimport type { Gap } from '../../types'\n\ntype StatusLabelProps = ComponentProps<typeof StatusLabel>\ntype StatusLabelType = FunctionComponentElement<StatusLabelProps>\n\ntype Props = PropsWithChildren<{\n /** グループのタイトル名 */\n title: ReactNode\n /** タイトルの見出しのタイプ */\n titleType?: TextProps['styleType']\n /** タイトル右の領域 */\n subActionArea?: ReactNode\n /** タイトルの見出しを視覚的に隠すかどうか */\n dangerouslyTitleHidden?: boolean\n /** label 要素に適用する `htmlFor` 値 */\n htmlFor?: string\n /** label 要素に適用する `id` 値 */\n labelId?: string\n /** タイトル群と子要素の間の間隔調整用(基本的には不要) */\n innerMargin?: Gap\n /** タイトルの隣に表示する `StatusLabel` の Props の配列 */\n /**\n * @deprecated statusLabelProps属性は非推奨です。statusLabelsを使ってください。\n */\n statusLabelProps?: StatusLabelProps | StatusLabelProps[]\n /** タイトルの隣に表示する `StatusLabel` の配列 */\n statusLabels?: StatusLabelType | StatusLabelType[]\n /** タイトルの下に表示するヘルプメッセージ */\n helpMessage?: ReactNode\n /** タイトルの下に表示する入力例 */\n exampleMessage?: ReactNode\n /** タイトルの下に表示するエラーメッセージ */\n errorMessages?: ReactNode | ReactNode[]\n /** エラーがある場合に自動的に入力要素を error にするかどうか */\n autoBindErrorInput?: boolean\n /** フォームコントロールの下に表示する補足メッセージ */\n supplementaryMessage?: ReactNode\n /** `true` のとき、文字色を `TEXT_DISABLED` にする */\n disabled?: boolean\n as?: string | ComponentType<any>\n}>\ntype ElementProps = Omit<ComponentPropsWithoutRef<'div'>, keyof Props | 'aria-labelledby'>\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-FormControl',\n 'shr-mx-[unset] shr-border-none shr-p-[unset]',\n 'disabled:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-label_>_span]:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-exampleMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-errorMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-supplementaryMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-Input]:shr-border-default/50 [&:disabled_.smarthr-ui-Input]:shr-bg-white-darken',\n ],\n label: ['smarthr-ui-FormControl-label'],\n errorList: ['shr-list-none'],\n errorIcon: ['smarthr-ui-FormControl-errorMessage', 'shr-text-danger'],\n underTitleStack: ['[&&&]:shr-mt-0'],\n childrenWrapper: [],\n },\n variants: {\n innerMargin: {\n 0: {},\n 0.25: {},\n 0.5: {},\n 0.75: {},\n 1: {},\n 1.25: {},\n 1.5: {},\n 2: {},\n 2.5: {},\n 3: {},\n 3.5: {},\n 4: {},\n 8: {},\n X3S: {},\n XXS: {},\n XS: {},\n S: {},\n M: {},\n L: {},\n XL: {},\n XXL: {},\n X3L: {},\n } as { [key in Gap]: string },\n isFieldset: {\n true: {},\n false: {},\n },\n },\n compoundVariants: [\n // TODO: innerMarginが未指定、初期値の場合、かつFieldsetの場合、childrenの上部の余白を広げることで\n // FormControltとの差をわかりやすくしている\n // 微妙な方法ではあるので、必要に応じてinnerMarginではない属性を用意する\n // https://kufuinc.slack.com/archives/CGC58MW01/p1737944965871159?thread_ts=1737541173.404369&cid=CGC58MW01\n {\n innerMargin: undefined,\n isFieldset: true,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-1',\n },\n },\n {\n innerMargin: undefined,\n isFieldset: false,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-0.5',\n },\n },\n ],\n})\n\nconst SMARTHR_UI_INPUT_SELECTOR = '[data-smarthr-ui-input=\"true\"]'\n\nexport const ActualFormControl: FC<Props & ElementProps> = ({\n title,\n titleType = 'blockTitle',\n subActionArea,\n dangerouslyTitleHidden = false,\n htmlFor,\n labelId,\n innerMargin,\n statusLabels,\n statusLabelProps,\n helpMessage,\n exampleMessage,\n errorMessages,\n autoBindErrorInput = true,\n supplementaryMessage,\n as = 'div',\n className,\n children,\n ...props\n}) => {\n const defaultHtmlFor = useId()\n const defaultLabelId = useId()\n const managedHtmlFor = htmlFor || defaultHtmlFor\n const managedLabelId = labelId || defaultLabelId\n const inputWrapperRef = useRef<HTMLDivElement>(null)\n const isFieldset = as === 'fieldset'\n\n const describedbyIds = useMemo(() => {\n const temp = []\n\n if (helpMessage) {\n temp.push(`${managedHtmlFor}_helpMessage`)\n }\n if (exampleMessage) {\n temp.push(`${managedHtmlFor}_exampleMessage`)\n }\n if (supplementaryMessage) {\n temp.push(`${managedHtmlFor}_supplementaryMessage`)\n }\n if (errorMessages) {\n temp.push(`${managedHtmlFor}_errorMessages`)\n }\n\n return temp.join(' ')\n }, [helpMessage, exampleMessage, supplementaryMessage, errorMessages, managedHtmlFor])\n\n const actualStatusLabels = useMemo(() => {\n if (statusLabels) {\n return (Array.isArray(statusLabels) ? statusLabels : [statusLabels]).map(\n (statusLabel, index) => <Fragment key={index}>{statusLabel}</Fragment>,\n )\n }\n\n if (!statusLabelProps) {\n return []\n }\n\n const labelProps = Array.isArray(statusLabelProps) ? statusLabelProps : [statusLabelProps]\n\n return labelProps.map((prop, index) => <StatusLabel {...prop} key={index} />)\n }, [statusLabels, statusLabelProps])\n\n const actualErrorMessages = useMemo(() => {\n if (!errorMessages) {\n return []\n }\n\n return Array.isArray(errorMessages) ? errorMessages : [errorMessages]\n }, [errorMessages])\n\n const actualInnerMargin = useMemo(() => innerMargin ?? 0.5, [innerMargin])\n\n const classNames = useMemo(() => {\n const { wrapper, label, errorList, errorIcon, underTitleStack, childrenWrapper } =\n classNameGenerator({ innerMargin, isFieldset })\n\n return {\n wrapper: wrapper({ className }),\n label: label({\n className: dangerouslyTitleHidden ? visuallyHiddenTextClassNameGenerator() : '',\n }),\n errorList: errorList(),\n errorIcon: errorIcon(),\n underTitleStack: underTitleStack(),\n childrenWrapper: childrenWrapper(),\n }\n }, [innerMargin, isFieldset, dangerouslyTitleHidden, className])\n\n useEffect(() => {\n if (\n isFieldset ||\n !inputWrapperRef?.current ||\n // HINT: 対象idを持つ要素が既に存在する場合、何もしない\n document.getElementById(managedHtmlFor)\n ) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!input) {\n return\n }\n\n if (!input.getAttribute('id')) {\n input.setAttribute('id', managedHtmlFor)\n }\n\n if (input instanceof HTMLInputElement && input.type === 'file') {\n const attrName = 'aria-labelledby'\n const inputLabelledByIds = input.getAttribute(attrName)\n\n if (inputLabelledByIds) {\n // InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める\n input.setAttribute(attrName, `${inputLabelledByIds} ${managedLabelId}`)\n }\n }\n }, [managedHtmlFor, isFieldset, managedLabelId])\n\n useEffect(() => {\n if (!describedbyIds || !inputWrapperRef?.current) {\n return\n }\n\n const inputWrapper = inputWrapperRef.current\n const attrName = 'aria-describedby'\n\n if (inputWrapper.querySelector(`[${attrName}=\"${describedbyIds}\"]`)) {\n return\n }\n\n const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attribute = input.getAttribute(attrName)\n\n input.setAttribute(attrName, attribute ? `${attribute} ${describedbyIds}` : describedbyIds)\n }\n }, [describedbyIds])\n\n useEffect(() => {\n if (!autoBindErrorInput || !inputWrapperRef?.current) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attrName = 'aria-invalid'\n\n if (actualErrorMessages.length > 0) {\n input.setAttribute(attrName, 'true')\n } else {\n input.removeAttribute(attrName)\n }\n }\n }, [actualErrorMessages.length, autoBindErrorInput])\n\n let body = (\n <>\n <HelpMessageParagraph helpMessage={helpMessage} managedHtmlFor={managedHtmlFor} />\n <ExampleMessageText exampleMessage={exampleMessage} managedHtmlFor={managedHtmlFor} />\n <ErrorMessageList\n errorMessages={actualErrorMessages}\n managedHtmlFor={managedHtmlFor}\n classNames={classNames}\n />\n <div className={classNames.childrenWrapper} ref={inputWrapperRef}>\n {children}\n </div>\n <SupplementaryMessageText\n supplementaryMessage={supplementaryMessage}\n managedHtmlFor={managedHtmlFor}\n />\n </>\n )\n\n // HINT: dangerouslyTitleHiddenの場合、body以下の余白の計算を簡略化するため\n // Stackをネストし、そのStackに対してmargin-top: 0を指定する\n // こうすることでinner Stack以下の要素は擬似的にStackの最初の要素になる\n if (dangerouslyTitleHidden) {\n body = (\n <Stack gap={actualInnerMargin} className={classNames.underTitleStack}>\n {body}\n </Stack>\n )\n }\n\n return (\n <Stack\n {...props}\n as={as}\n gap={actualInnerMargin}\n aria-describedby={isFieldset && describedbyIds ? describedbyIds : undefined}\n className={classNames.wrapper}\n >\n <TitleCluster\n isFieldset={isFieldset}\n managedHtmlFor={managedHtmlFor}\n managedLabelId={managedLabelId}\n dangerouslyTitleHidden={dangerouslyTitleHidden}\n titleType={titleType}\n title={title}\n statusLabels={actualStatusLabels}\n subActionArea={subActionArea}\n labelClassName={classNames.label}\n />\n {body}\n </Stack>\n )\n}\n\nconst TitleCluster = memo<\n Pick<Props, 'dangerouslyTitleHidden' | 'title' | 'subActionArea'> & {\n titleType: TextProps['styleType']\n isFieldset: boolean\n managedHtmlFor: string\n managedLabelId: string\n labelClassName: string\n statusLabels: StatusLabelType[]\n }\n>(\n ({\n isFieldset,\n managedHtmlFor,\n managedLabelId,\n dangerouslyTitleHidden,\n titleType,\n title,\n subActionArea,\n labelClassName,\n statusLabels,\n }) => {\n const body = (\n <>\n <Text styleType={titleType}>{title}</Text>\n <StatusLabelCluster statusLabels={statusLabels} />\n </>\n )\n\n const attrs = useMemo(() => {\n if (dangerouslyTitleHidden) {\n return {\n label: null,\n visuallyHidden: isFieldset\n ? {\n as: 'legend',\n }\n : {\n as: 'label',\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n }\n }\n\n if (isFieldset) {\n return {\n label: { 'aria-hidden': 'true' } as const,\n visuallyHidden: { as: 'legend' },\n }\n }\n\n return {\n label: {\n as: 'label' as const,\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n visuallyHidden: null,\n }\n }, [managedLabelId, managedHtmlFor, dangerouslyTitleHidden, isFieldset])\n\n return (\n <>\n {attrs.visuallyHidden && (\n <VisuallyHiddenText {...attrs.visuallyHidden}>\n {\n // HINT: innerTextでは正しく文字が取得できない場合がある\n // 安全策としてinnerTextが空を取得してきたらbody自体を埋めこみます\n innerText(body) || body\n }\n </VisuallyHiddenText>\n )}\n {attrs.label && (\n <Cluster\n justify=\"space-between\"\n // HINT: UI上、常にトップの要素になるため、Stackの計算が狂わないよう、\n // 常にmargin-topを0にする\n className=\"[&&&]:shr--mt-0\"\n >\n <Cluster {...attrs.label} align=\"center\" className={labelClassName}>\n {body}\n </Cluster>\n {subActionArea && <div className=\"shr-grow\">{subActionArea}</div>}\n </Cluster>\n )}\n </>\n )\n },\n)\n\nconst StatusLabelCluster = memo<{ statusLabels: StatusLabelType[] }>(({ statusLabels }) =>\n statusLabels.length === 0 ? null : (\n <Cluster gap={0.25} as=\"span\">\n {statusLabels}\n </Cluster>\n ),\n)\n\nconst HelpMessageParagraph = memo<Pick<Props, 'helpMessage'> & { managedHtmlFor: string }>(\n ({ helpMessage, managedHtmlFor }) =>\n helpMessage ? (\n <p className=\"smarthr-ui-FormControl-helpMessage\" id={`${managedHtmlFor}_helpMessage`}>\n {helpMessage}\n </p>\n ) : null,\n)\n\nconst ExampleMessageText = memo<Pick<Props, 'exampleMessage'> & { managedHtmlFor: string }>(\n ({ exampleMessage, managedHtmlFor }) =>\n exampleMessage ? (\n <Text\n as=\"p\"\n color=\"TEXT_GREY\"\n italic\n id={`${managedHtmlFor}_exampleMessage`}\n className=\"smarthr-ui-FormControl-exampleMessage\"\n >\n {exampleMessage}\n </Text>\n ) : null,\n)\n\nconst ErrorMessageList = memo<{\n errorMessages: ReactNode[]\n managedHtmlFor: string\n classNames: {\n errorList: string\n errorIcon: string\n }\n}>(({ errorMessages, managedHtmlFor, classNames }) =>\n errorMessages.length > 0 ? (\n <div id={`${managedHtmlFor}_errorMessages`} className={classNames.errorList} role=\"alert\">\n {errorMessages.map((message, index) => (\n <p key={index}>\n <FaCircleExclamationIcon text={message} className={classNames.errorIcon} />\n </p>\n ))}\n </div>\n ) : null,\n)\n\nconst SupplementaryMessageText = memo<\n Pick<Props, 'supplementaryMessage'> & { managedHtmlFor: string }\n>(({ supplementaryMessage, managedHtmlFor }) =>\n supplementaryMessage ? (\n <Text\n as=\"p\"\n size=\"S\"\n color=\"TEXT_GREY\"\n id={`${managedHtmlFor}_supplementaryMessage`}\n className=\"smarthr-ui-FormControl-supplementaryMessage\"\n >\n {supplementaryMessage}\n </Text>\n ) : null,\n)\n\nexport const FormControl: FC<Omit<Props & ElementProps, 'as' | 'disabled'>> = ActualFormControl\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAqEA;AACE;AACE;;;;;;;;;AASC;;;AAGD;;AAEA;AACD;AACD;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAC2B;AAC7B;AACE;AACA;AACD;AACF;AACD;;;;;AAKE;AACE;AACA;AACA;AACE;AACD;AACF;AACD;AACE;AACA;AACA;AACE;AACD;AACF;AACF;AACF;AAED;AAEO;AAoBL;AACA;AACA;AACA;AACA;AACA;AAEA;;;AAII;;;AAGA;;;AAGA;;;AAGA;;AAGF;AACF;AAEA;;AAEI;;;AAMA;;AAGF;;AAGF;AAEA;;AAEI;;AAGF;AACF;AAEA;AAEA;;;AAKI;;;;;;;;;;;AAYF;;;AAIE;;;;;;;;AAYA;;;;;;;;;;;;;;;AAmBF;;;;;;;;AAYE;;AAEJ;;;;;;;;AAYI;AACE;;;AAEA;;;;AAKN;;;;;AAuBE;;AAOF;AAsBF;AAEA;;AA4BI;;;AAGM;AACA;AACE;AACI;AACD;AACH;AACI;AACA;AACA;AACD;;;;;AAML;AACA;;;;AAKF;AACE;AACA;AACA;AACD;AACD;;;AAIJ;;;AAOU;;;AASF;AAUV;AAGF;AAQA;AASA;AAeA;AAmBA;AAgBO;;"}
1
+ {"version":3,"file":"FormControl.js","sources":["../../../src/components/FormControl/FormControl.tsx"],"sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type ComponentType,\n type FC,\n Fragment,\n type FunctionComponentElement,\n type PropsWithChildren,\n type ReactNode,\n memo,\n useEffect,\n useMemo,\n useRef,\n} from 'react'\nimport { useId } from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { FaCircleExclamationIcon } from '../Icon'\nimport { Cluster, Stack } from '../Layout'\nimport { StatusLabel } from '../StatusLabel'\nimport { Text, type TextProps } from '../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassNameGenerator } from '../VisuallyHiddenText'\n\nimport type { Gap } from '../../types'\n\ntype StatusLabelProps = ComponentProps<typeof StatusLabel>\ntype StatusLabelType = FunctionComponentElement<StatusLabelProps>\n\ntype Props = PropsWithChildren<{\n /** グループのタイトル名 */\n title: ReactNode\n /** タイトルの見出しのタイプ */\n titleType?: TextProps['styleType']\n /** タイトル右の領域 */\n subActionArea?: ReactNode\n /** タイトルの見出しを視覚的に隠すかどうか */\n dangerouslyTitleHidden?: boolean\n /** label 要素に適用する `htmlFor` 値 */\n htmlFor?: string\n /** label 要素に適用する `id` 値 */\n labelId?: string\n /** タイトル群と子要素の間の間隔調整用(基本的には不要) */\n innerMargin?: Gap\n /** タイトルの隣に表示する `StatusLabel` の Props の配列 */\n /**\n * @deprecated statusLabelProps属性は非推奨です。statusLabelsを使ってください。\n */\n statusLabelProps?: StatusLabelProps | StatusLabelProps[]\n /** タイトルの隣に表示する `StatusLabel` の配列 */\n statusLabels?: StatusLabelType | StatusLabelType[]\n /** タイトルの下に表示するヘルプメッセージ */\n helpMessage?: ReactNode\n /** タイトルの下に表示する入力例 */\n exampleMessage?: ReactNode\n /** タイトルの下に表示するエラーメッセージ */\n errorMessages?: ReactNode | ReactNode[]\n /** エラーがある場合に自動的に入力要素を error にするかどうか */\n autoBindErrorInput?: boolean\n /** フォームコントロールの下に表示する補足メッセージ */\n supplementaryMessage?: ReactNode\n /** `true` のとき、文字色を `TEXT_DISABLED` にする */\n disabled?: boolean\n as?: string | ComponentType<any>\n}>\ntype ElementProps = Omit<ComponentPropsWithoutRef<'div'>, keyof Props | 'aria-labelledby'>\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-FormControl',\n 'shr-mx-[unset] shr-border-none shr-p-[unset]',\n 'disabled:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-label_>_span]:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-exampleMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-errorMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-supplementaryMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-Input]:shr-border-default/50 [&:disabled_.smarthr-ui-Input]:shr-bg-white-darken',\n ],\n label: ['smarthr-ui-FormControl-label'],\n errorList: ['shr-list-none'],\n errorIcon: ['smarthr-ui-FormControl-errorMessage', 'shr-text-danger'],\n underTitleStack: ['[&&&]:shr-mt-0'],\n childrenWrapper: [],\n },\n variants: {\n innerMargin: {\n 0: {},\n 0.25: {},\n 0.5: {},\n 0.75: {},\n 1: {},\n 1.25: {},\n 1.5: {},\n 2: {},\n 2.5: {},\n 3: {},\n 3.5: {},\n 4: {},\n 8: {},\n X3S: {},\n XXS: {},\n XS: {},\n S: {},\n M: {},\n L: {},\n XL: {},\n XXL: {},\n X3L: {},\n } as { [key in Gap]: string },\n isFieldset: {\n true: {},\n false: {},\n },\n },\n compoundVariants: [\n // TODO: innerMarginが未指定、初期値の場合、かつFieldsetの場合、childrenの上部の余白を広げることで\n // FormControltとの差をわかりやすくしている\n // 微妙な方法ではあるので、必要に応じてinnerMarginではない属性を用意する\n // https://kufuinc.slack.com/archives/CGC58MW01/p1737944965871159?thread_ts=1737541173.404369&cid=CGC58MW01\n {\n innerMargin: undefined,\n isFieldset: true,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-1',\n },\n },\n {\n innerMargin: undefined,\n isFieldset: false,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-0.5',\n },\n },\n ],\n})\n\nconst SMARTHR_UI_INPUT_SELECTOR = '[data-smarthr-ui-input=\"true\"]'\n\nexport const ActualFormControl: FC<Props & ElementProps> = ({\n title,\n titleType = 'blockTitle',\n subActionArea,\n dangerouslyTitleHidden = false,\n htmlFor,\n labelId,\n innerMargin,\n statusLabels,\n statusLabelProps,\n helpMessage,\n exampleMessage,\n errorMessages,\n autoBindErrorInput = true,\n supplementaryMessage,\n as = 'div',\n className,\n children,\n ...props\n}) => {\n const defaultHtmlFor = useId()\n const defaultLabelId = useId()\n const managedHtmlFor = htmlFor || defaultHtmlFor\n const managedLabelId = labelId || defaultLabelId\n const inputWrapperRef = useRef<HTMLDivElement>(null)\n const isFieldset = as === 'fieldset'\n\n const describedbyIds = useMemo(() => {\n const temp = []\n\n if (helpMessage) {\n temp.push(`${managedHtmlFor}_helpMessage`)\n }\n if (exampleMessage) {\n temp.push(`${managedHtmlFor}_exampleMessage`)\n }\n if (supplementaryMessage) {\n temp.push(`${managedHtmlFor}_supplementaryMessage`)\n }\n if (errorMessages) {\n temp.push(`${managedHtmlFor}_errorMessages`)\n }\n\n return temp.join(' ')\n }, [helpMessage, exampleMessage, supplementaryMessage, errorMessages, managedHtmlFor])\n\n const actualStatusLabels = useMemo(() => {\n if (statusLabels) {\n return (Array.isArray(statusLabels) ? statusLabels : [statusLabels]).map(\n (statusLabel, index) => <Fragment key={index}>{statusLabel}</Fragment>,\n )\n }\n\n if (!statusLabelProps) {\n return []\n }\n\n const labelProps = Array.isArray(statusLabelProps) ? statusLabelProps : [statusLabelProps]\n\n return labelProps.map((prop, index) => <StatusLabel {...prop} key={index} />)\n }, [statusLabels, statusLabelProps])\n\n const actualErrorMessages = useMemo(() => {\n if (!errorMessages) {\n return []\n }\n\n return Array.isArray(errorMessages) ? errorMessages : [errorMessages]\n }, [errorMessages])\n\n const actualInnerMargin = useMemo(() => innerMargin ?? 0.5, [innerMargin])\n\n const classNames = useMemo(() => {\n const { wrapper, label, errorList, errorIcon, underTitleStack, childrenWrapper } =\n classNameGenerator({ innerMargin, isFieldset })\n\n return {\n wrapper: wrapper({ className }),\n label: label({\n className: dangerouslyTitleHidden ? visuallyHiddenTextClassNameGenerator() : '',\n }),\n errorList: errorList(),\n errorIcon: errorIcon(),\n underTitleStack: underTitleStack(),\n childrenWrapper: childrenWrapper(),\n }\n }, [innerMargin, isFieldset, dangerouslyTitleHidden, className])\n\n useEffect(() => {\n if (\n isFieldset ||\n !inputWrapperRef?.current ||\n // HINT: 対象idを持つ要素が既に存在する場合、何もしない\n document.getElementById(managedHtmlFor)\n ) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!input) {\n return\n }\n\n if (!input.getAttribute('id')) {\n input.setAttribute('id', managedHtmlFor)\n }\n\n if (input instanceof HTMLInputElement && input.type === 'file') {\n const attrName = 'aria-labelledby'\n const inputLabelledByIds = input.getAttribute(attrName)\n\n if (inputLabelledByIds) {\n // InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める\n input.setAttribute(attrName, `${inputLabelledByIds} ${managedLabelId}`)\n }\n }\n }, [managedHtmlFor, isFieldset, managedLabelId])\n\n useEffect(() => {\n if (!describedbyIds || !inputWrapperRef?.current) {\n return\n }\n\n const inputWrapper = inputWrapperRef.current\n const attrName = 'aria-describedby'\n\n if (inputWrapper.querySelector(`[${attrName}=\"${describedbyIds}\"]`)) {\n return\n }\n\n const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attribute = input.getAttribute(attrName)\n\n input.setAttribute(attrName, attribute ? `${attribute} ${describedbyIds}` : describedbyIds)\n }\n }, [describedbyIds])\n\n useEffect(() => {\n if (!autoBindErrorInput || !inputWrapperRef?.current) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attrName = 'aria-invalid'\n\n if (actualErrorMessages.length > 0) {\n input.setAttribute(attrName, 'true')\n } else {\n input.removeAttribute(attrName)\n }\n }\n }, [actualErrorMessages.length, autoBindErrorInput])\n\n // HINT: Fieldset内の可視ラベルが無いinputに、legend文言をアクセシブルネームに追加する\n // https://waic.jp/translations/WCAG21/Understanding/label-in-name.html\n useEffect(() => {\n if (!isFieldset || !inputWrapperRef.current) return\n const inputs = inputWrapperRef.current.querySelectorAll(SMARTHR_UI_INPUT_SELECTOR)\n if (!inputs.length) return\n\n const legendText = innerText(title)\n if (!legendText) return\n\n const labels = new Set(\n Array.from(document.querySelectorAll('label[for]')).map((label) => label.getAttribute('for')),\n )\n\n inputs.forEach((input) => {\n const inputId = input.getAttribute('id')\n if (inputId && labels.has(inputId)) {\n return\n }\n\n let accessibleName = ''\n if (input.hasAttribute('aria-label')) {\n accessibleName = input.getAttribute('aria-label') || ''\n } else if (input.hasAttribute('title')) {\n accessibleName = input.getAttribute('title') || ''\n }\n\n if (!accessibleName.includes(legendText)) {\n input.setAttribute('aria-label', `${legendText} ${accessibleName}`.trim())\n }\n })\n }, [isFieldset, title])\n\n let body = (\n <>\n <HelpMessageParagraph helpMessage={helpMessage} managedHtmlFor={managedHtmlFor} />\n <ExampleMessageText exampleMessage={exampleMessage} managedHtmlFor={managedHtmlFor} />\n <ErrorMessageList\n errorMessages={actualErrorMessages}\n managedHtmlFor={managedHtmlFor}\n classNames={classNames}\n />\n <div className={classNames.childrenWrapper} ref={inputWrapperRef}>\n {children}\n </div>\n <SupplementaryMessageText\n supplementaryMessage={supplementaryMessage}\n managedHtmlFor={managedHtmlFor}\n />\n </>\n )\n\n // HINT: dangerouslyTitleHiddenの場合、body以下の余白の計算を簡略化するため\n // Stackをネストし、そのStackに対してmargin-top: 0を指定する\n // こうすることでinner Stack以下の要素は擬似的にStackの最初の要素になる\n if (dangerouslyTitleHidden) {\n body = (\n <Stack gap={actualInnerMargin} className={classNames.underTitleStack}>\n {body}\n </Stack>\n )\n }\n\n return (\n <Stack\n {...props}\n as={as}\n gap={actualInnerMargin}\n aria-describedby={isFieldset && describedbyIds ? describedbyIds : undefined}\n className={classNames.wrapper}\n >\n <TitleCluster\n isFieldset={isFieldset}\n managedHtmlFor={managedHtmlFor}\n managedLabelId={managedLabelId}\n dangerouslyTitleHidden={dangerouslyTitleHidden}\n titleType={titleType}\n title={title}\n statusLabels={actualStatusLabels}\n subActionArea={subActionArea}\n labelClassName={classNames.label}\n />\n {body}\n </Stack>\n )\n}\n\nconst TitleCluster = memo<\n Pick<Props, 'dangerouslyTitleHidden' | 'title' | 'subActionArea'> & {\n titleType: TextProps['styleType']\n isFieldset: boolean\n managedHtmlFor: string\n managedLabelId: string\n labelClassName: string\n statusLabels: StatusLabelType[]\n }\n>(\n ({\n isFieldset,\n managedHtmlFor,\n managedLabelId,\n dangerouslyTitleHidden,\n titleType,\n title,\n subActionArea,\n labelClassName,\n statusLabels,\n }) => {\n const body = (\n <>\n <Text styleType={titleType}>{title}</Text>\n <StatusLabelCluster statusLabels={statusLabels} />\n </>\n )\n\n const attrs = useMemo(() => {\n if (dangerouslyTitleHidden) {\n return {\n label: null,\n visuallyHidden: isFieldset\n ? {\n as: 'legend',\n }\n : {\n as: 'label',\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n }\n }\n\n if (isFieldset) {\n return {\n label: { 'aria-hidden': 'true' } as const,\n visuallyHidden: { as: 'legend' },\n }\n }\n\n return {\n label: {\n as: 'label' as const,\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n visuallyHidden: null,\n }\n }, [managedLabelId, managedHtmlFor, dangerouslyTitleHidden, isFieldset])\n\n return (\n <>\n {attrs.visuallyHidden && (\n <VisuallyHiddenText {...attrs.visuallyHidden}>\n {\n // HINT: innerTextでは正しく文字が取得できない場合がある\n // 安全策としてinnerTextが空を取得してきたらbody自体を埋めこみます\n innerText(body) || body\n }\n </VisuallyHiddenText>\n )}\n {attrs.label && (\n <Cluster\n justify=\"space-between\"\n // HINT: UI上、常にトップの要素になるため、Stackの計算が狂わないよう、\n // 常にmargin-topを0にする\n className=\"[&&&]:shr--mt-0\"\n >\n <Cluster {...attrs.label} align=\"center\" className={labelClassName}>\n {body}\n </Cluster>\n {subActionArea && <div className=\"shr-grow\">{subActionArea}</div>}\n </Cluster>\n )}\n </>\n )\n },\n)\n\nconst StatusLabelCluster = memo<{ statusLabels: StatusLabelType[] }>(({ statusLabels }) =>\n statusLabels.length === 0 ? null : (\n <Cluster gap={0.25} as=\"span\">\n {statusLabels}\n </Cluster>\n ),\n)\n\nconst HelpMessageParagraph = memo<Pick<Props, 'helpMessage'> & { managedHtmlFor: string }>(\n ({ helpMessage, managedHtmlFor }) =>\n helpMessage ? (\n <p className=\"smarthr-ui-FormControl-helpMessage\" id={`${managedHtmlFor}_helpMessage`}>\n {helpMessage}\n </p>\n ) : null,\n)\n\nconst ExampleMessageText = memo<Pick<Props, 'exampleMessage'> & { managedHtmlFor: string }>(\n ({ exampleMessage, managedHtmlFor }) =>\n exampleMessage ? (\n <Text\n as=\"p\"\n color=\"TEXT_GREY\"\n italic\n id={`${managedHtmlFor}_exampleMessage`}\n className=\"smarthr-ui-FormControl-exampleMessage\"\n >\n {exampleMessage}\n </Text>\n ) : null,\n)\n\nconst ErrorMessageList = memo<{\n errorMessages: ReactNode[]\n managedHtmlFor: string\n classNames: {\n errorList: string\n errorIcon: string\n }\n}>(({ errorMessages, managedHtmlFor, classNames }) =>\n errorMessages.length > 0 ? (\n <div id={`${managedHtmlFor}_errorMessages`} className={classNames.errorList} role=\"alert\">\n {errorMessages.map((message, index) => (\n <p key={index}>\n <FaCircleExclamationIcon text={message} className={classNames.errorIcon} />\n </p>\n ))}\n </div>\n ) : null,\n)\n\nconst SupplementaryMessageText = memo<\n Pick<Props, 'supplementaryMessage'> & { managedHtmlFor: string }\n>(({ supplementaryMessage, managedHtmlFor }) =>\n supplementaryMessage ? (\n <Text\n as=\"p\"\n size=\"S\"\n color=\"TEXT_GREY\"\n id={`${managedHtmlFor}_supplementaryMessage`}\n className=\"smarthr-ui-FormControl-supplementaryMessage\"\n >\n {supplementaryMessage}\n </Text>\n ) : null,\n)\n\nexport const FormControl: FC<Omit<Props & ElementProps, 'as' | 'disabled'>> = ActualFormControl\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAqEA;AACE;AACE;;;;;;;;;AASC;;;AAGD;;AAEA;AACD;AACD;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAC2B;AAC7B;AACE;AACA;AACD;AACF;AACD;;;;;AAKE;AACE;AACA;AACA;AACE;AACD;AACF;AACD;AACE;AACA;AACA;AACE;AACD;AACF;AACF;AACF;AAED;AAEO;AAoBL;AACA;AACA;AACA;AACA;AACA;AAEA;;;AAII;;;AAGA;;;AAGA;;;AAGA;;AAGF;AACF;AAEA;;AAEI;;;AAMA;;AAGF;;AAGF;AAEA;;AAEI;;AAGF;AACF;AAEA;AAEA;;;AAKI;;;;;;;;;;;AAYF;;;AAIE;;;;;;;;AAYA;;;;;;;;;;;;;;;AAmBF;;;;;;;;AAYE;;AAEJ;;;;;;;;AAYI;AACE;;;AAEA;;;;;;;AAQJ;;;;;AAIA;AACA;;AAEA;AAIA;;;;;;AAOE;;;AAEO;;;;AAKL;;AAEJ;AACF;AAEA;;;;;AAuBE;;AAOF;AAsBF;AAEA;;AA4BI;;;AAGM;AACA;AACE;AACI;AACD;AACH;AACI;AACA;AACA;AACD;;;;;AAML;AACA;;;;AAKF;AACE;AACA;AACA;AACD;AACD;;;AAIJ;;;AAOU;;;AASF;AAUV;AAGF;AAQA;AASA;AAeA;AAmBA;AAgBO;;"}
@@ -2,7 +2,7 @@
2
2
  import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import { memo, useMemo, useState, useRef, useId, useSyncExternalStore, useCallback, cloneElement } from 'react';
4
4
  import { createPortal } from 'react-dom';
5
- import innerText from '../../_virtual/index.js';
5
+ import innerText from '../../_virtual/index3.js';
6
6
  import { tv as ce } from './../../vendor/.pnpm/tailwind-variants@0.3.1_tailwindcss@3.4.17_ts-node@10.9.2_@swc_core@1.12.11_@types_node@20.19.7_typescript@5.8.3__/vendor/tailwind-variants/dist/index.js';
7
7
  import { useEnhancedEffect } from '../../hooks/useEnhancedEffect.js';
8
8
  import { VisuallyHiddenText } from '../VisuallyHiddenText/VisuallyHiddenText.js';
@@ -1,5 +1,5 @@
1
- import _merge from '../_virtual/index2.js';
2
- import _range from '../_virtual/index3.js';
1
+ import _merge from '../_virtual/index.js';
2
+ import _range from '../_virtual/index2.js';
3
3
 
4
4
  const merge = _merge;
5
5
  const range = _range;
@@ -7,6 +7,7 @@ type Props = {
7
7
  scaleSteps?: number[];
8
8
  scaleStep?: number;
9
9
  onPassword?: ComponentProps<typeof PDFViewer>['onPassword'];
10
+ onLoadError?: () => void;
10
11
  };
11
12
  export declare const FileViewer: FC<Props>;
12
13
  export {};
@@ -14,7 +14,7 @@ const ImageViewer_1 = require("./ImageViewer");
14
14
  const PDFViewer_1 = require("./PDFViewer");
15
15
  const defaultScaleStep = new decimal_js_1.default(0.2);
16
16
  const defaultScaleSteps = [0.2, 0.6, 1, 1.6, 2, 3];
17
- const FileViewer = ({ file, scaleStep, scaleSteps, width: fixedWidth, onPassword, }) => {
17
+ const FileViewer = ({ file, scaleStep, scaleSteps, width: fixedWidth, onPassword, onLoadError, }) => {
18
18
  const ref = (0, react_1.useRef)(null);
19
19
  const [scale, setScale] = (0, react_1.useState)(1);
20
20
  const [loaded, setLoaded] = (0, react_1.useState)(false);
@@ -45,7 +45,7 @@ const FileViewer = ({ file, scaleStep, scaleSteps, width: fixedWidth, onPassword
45
45
  resizeObserver.disconnect();
46
46
  };
47
47
  }, [fixedWidth]);
48
- return ((0, jsx_runtime_1.jsxs)("div", { className: "shr-flex shr-h-full shr-w-full shr-flex-col shr-gap-2 shr-overflow-auto shr-bg-scrim shr-bg-[radial-gradient(theme(textColor.black)_1px,_transparent_0)] shr-bg-[length:16px_16px]", ref: ref, children: [(0, jsx_runtime_1.jsx)("div", { className: "shr-sticky shr-start-0 shr-top-0 shr-z-[1] shr-flex shr-w-full shr-flex-shrink-0 shr-gap-0.5", children: (0, jsx_runtime_1.jsx)(Controller, { scale: scale, setScale: setScale, scaleSteps: scaleSteps || defaultScaleSteps, onClickScaleUpButton: scaleUp, onClickScaleDownButton: scaleDown, onClickRotateButton: rotate }) }), (0, jsx_runtime_1.jsxs)("div", { className: "shr-z-[0] shr-mx-auto shr-my-0 shr-box-border shr-flex shr-w-fit shr-flex-shrink-0 shr-grow shr-items-center shr-justify-center shr-px-2 shr-pb-2", children: [!loaded && ((0, jsx_runtime_1.jsx)("div", { className: "shr-pointer-events-none shr-fixed shr-inset-0 shr-flex shr-h-full shr-w-full shr-items-center shr-justify-center", children: (0, jsx_runtime_1.jsx)(__1.Loader, { type: "light", size: "m" }) })), (0, jsx_runtime_1.jsx)("div", { className: !loaded ? 'shr-invisible' : '', children: file.contentType === 'application/pdf' ? ((0, jsx_runtime_1.jsx)(PDFViewer_1.PDFViewer, { scale: scale, rotation: rotation, file: file, width: width, onLoad: handleLoaded, onPassword: onPassword })) : file.contentType.startsWith('image/') ? ((0, jsx_runtime_1.jsx)(ImageViewer_1.ImageViewer, { scale: scale, rotation: rotation, file: file, width: width, onLoad: handleLoaded })) : ((0, jsx_runtime_1.jsx)(__1.Text, { children: (0, jsx_runtime_1.jsx)(intl_1.Localizer, { id: "smarthr-ui/FileViewer/unsupportedFileText", defaultText: "\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u306A\u3044\u5F62\u5F0F\u306E\u30D5\u30A1\u30A4\u30EB\u3067\u3059\u3002" }) })) })] })] }));
48
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "shr-flex shr-h-full shr-w-full shr-flex-col shr-gap-2 shr-overflow-auto shr-bg-scrim shr-bg-[radial-gradient(theme(textColor.black)_1px,_transparent_0)] shr-bg-[length:16px_16px]", ref: ref, children: [(0, jsx_runtime_1.jsx)("div", { className: "shr-sticky shr-start-0 shr-top-0 shr-z-[1] shr-flex shr-w-full shr-flex-shrink-0 shr-gap-0.5", children: (0, jsx_runtime_1.jsx)(Controller, { scale: scale, setScale: setScale, scaleSteps: scaleSteps || defaultScaleSteps, onClickScaleUpButton: scaleUp, onClickScaleDownButton: scaleDown, onClickRotateButton: rotate }) }), (0, jsx_runtime_1.jsxs)("div", { className: "shr-z-[0] shr-mx-auto shr-my-0 shr-box-border shr-flex shr-w-fit shr-flex-shrink-0 shr-grow shr-items-center shr-justify-center shr-px-2 shr-pb-2", children: [!loaded && ((0, jsx_runtime_1.jsx)("div", { className: "shr-pointer-events-none shr-fixed shr-inset-0 shr-flex shr-h-full shr-w-full shr-items-center shr-justify-center", children: (0, jsx_runtime_1.jsx)(__1.Loader, { type: "light", size: "m" }) })), (0, jsx_runtime_1.jsx)("div", { className: !loaded ? 'shr-invisible' : '', children: file.contentType === 'application/pdf' ? ((0, jsx_runtime_1.jsx)(PDFViewer_1.PDFViewer, { scale: scale, rotation: rotation, file: file, width: width, onLoad: handleLoaded, onPassword: onPassword, onLoadError: onLoadError })) : file.contentType.startsWith('image/') ? ((0, jsx_runtime_1.jsx)(ImageViewer_1.ImageViewer, { scale: scale, rotation: rotation, file: file, width: width, onLoad: handleLoaded, onLoadError: onLoadError })) : ((0, jsx_runtime_1.jsx)(__1.Text, { children: (0, jsx_runtime_1.jsx)(intl_1.Localizer, { id: "smarthr-ui/FileViewer/unsupportedFileText", defaultText: "\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u306A\u3044\u5F62\u5F0F\u306E\u30D5\u30A1\u30A4\u30EB\u3067\u3059\u3002" }) })) })] })] }));
49
49
  };
50
50
  exports.FileViewer = FileViewer;
51
51
  const Controller = (0, react_1.memo)(({ scale, setScale, scaleSteps, onClickScaleUpButton, onClickScaleDownButton, onClickRotateButton, }) => ((0, jsx_runtime_1.jsx)("div", { className: "shr-sticky shr-flex shr-w-full shr-items-center shr-justify-center shr-justify-items-center shr-gap-0.5 shr-bg-scrim shr-p-0.5 shr-shadow-layer-1", children: (0, jsx_runtime_1.jsxs)(__1.Cluster, { gap: 0.5, children: [(0, jsx_runtime_1.jsxs)("div", { className: "shr-border-shorthand shr-flex shr-divide-x shr-divide-solid shr-overflow-hidden shr-rounded-m", children: [(0, jsx_runtime_1.jsx)(__1.Button, { onClick: onClickScaleDownButton, disabled: scale <= scaleSteps[0], className: "shr-rounded-none shr-border-none", children: (0, jsx_runtime_1.jsx)(__1.FaMagnifyingGlassMinusIcon, { alt: (0, jsx_runtime_1.jsx)(intl_1.Localizer, { id: "smarthr-ui/FileViewer/scaleDownAlt", defaultText: "\u7E2E\u5C0F" }) }) }), (0, jsx_runtime_1.jsx)(__1.DropdownMenuButton, { label: (0, jsx_runtime_1.jsxs)(__1.Text, { children: [(0, jsx_runtime_1.jsx)(__1.VisuallyHiddenText, { children: (0, jsx_runtime_1.jsx)(intl_1.Localizer, { id: "smarthr-ui/FileViewer/scaleRateLabel", defaultText: "\u62E1\u5927\u7387" }) }), `${(scale * 100).toFixed(0)}%`] }), className: "shr-border-y-0 shr-border-[theme(borderColor.default)] [&_.smarthr-ui-Button]:shr-rounded-none [&_.smarthr-ui-Button]:shr-border-[transparent]", children: scaleSteps.map((step) => ((0, jsx_runtime_1.jsx)(__1.Button, { onClick: () => setScale(step), className: "shr-rounded-none shr-border-0", children: `${(step * 100).toFixed(0)}%` }, step.toString()))) }), (0, jsx_runtime_1.jsx)(__1.Button, { onClick: onClickScaleUpButton, className: "shr-rounded-none shr-border-0", children: (0, jsx_runtime_1.jsx)(__1.FaMagnifyingGlassPlusIcon, { alt: (0, jsx_runtime_1.jsx)(intl_1.Localizer, { id: "smarthr-ui/FileViewer/scaleUpAlt", defaultText: "\u62E1\u5927" }) }) })] }), (0, jsx_runtime_1.jsx)(__1.Button, { onClick: onClickRotateButton, className: "shr-p-0.75", children: (0, jsx_runtime_1.jsx)(__1.FaArrowRotateLeftIcon, { alt: (0, jsx_runtime_1.jsx)(intl_1.Localizer, { id: "smarthr-ui/FileViewer/rotateAlt", defaultText: "\u5DE6\u56DE\u8EE2" }) }) })] }) })));
@@ -1 +1 @@
1
- {"version":3,"file":"FileViewer.js","sourceRoot":"","sources":["../../../src/components/FileViewer/FileViewer.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport Decimal from 'decimal.js'\nimport {\n type ComponentProps,\n type FC,\n memo,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n Button,\n Cluster,\n DropdownMenuButton,\n FaArrowRotateLeftIcon,\n FaMagnifyingGlassMinusIcon,\n FaMagnifyingGlassPlusIcon,\n Loader,\n Text,\n VisuallyHiddenText,\n} from '../..'\nimport { Localizer } from '../../intl'\n\nimport { ImageViewer } from './ImageViewer'\nimport { PDFViewer } from './PDFViewer'\n\nimport type { FileForViewer } from './types'\n\nconst defaultScaleStep = new Decimal(0.2)\nconst defaultScaleSteps = [0.2, 0.6, 1, 1.6, 2, 3]\n\ntype Props = {\n file: FileForViewer\n width?: number\n\n /*\n * 拡大縮小率のステップを、100%を1とした配列で指定します。\n * */\n scaleSteps?: number[]\n\n scaleStep?: number\n onPassword?: ComponentProps<typeof PDFViewer>['onPassword']\n}\n\nexport const FileViewer: FC<Props> = ({\n file,\n scaleStep,\n scaleSteps,\n width: fixedWidth,\n onPassword,\n}) => {\n const ref = useRef<HTMLDivElement>(null)\n const [scale, setScale] = useState(1)\n const [loaded, setLoaded] = useState(false)\n const [rotation, setRotation] = useState(0)\n const [width, setWidth] = useState(fixedWidth ?? 0)\n\n const internalScaleStep = useMemo(\n () => (scaleStep ? new Decimal(scaleStep) : defaultScaleStep),\n [scaleStep],\n )\n\n const scaleUp = useCallback(() => {\n setScale((currentScale) => new Decimal(currentScale).add(internalScaleStep).toNumber())\n }, [internalScaleStep])\n\n const scaleDown = useCallback(() => {\n setScale((currentScale) => new Decimal(currentScale).sub(internalScaleStep).toNumber())\n }, [internalScaleStep])\n\n const rotate = useCallback(() => {\n setRotation((currentRotation) => currentRotation - 90)\n }, [])\n\n const handleLoaded = useCallback(() => {\n setLoaded(true)\n }, [])\n\n useEffect(() => {\n if (!ref.current || fixedWidth !== undefined) {\n return\n }\n\n const resizeObserver = new ResizeObserver(() => {\n setWidth((ref.current?.clientWidth ?? 0) - 64)\n })\n\n resizeObserver.observe(ref.current)\n\n return () => {\n resizeObserver.disconnect()\n }\n }, [fixedWidth])\n\n return (\n <div\n className=\"shr-flex shr-h-full shr-w-full shr-flex-col shr-gap-2 shr-overflow-auto shr-bg-scrim shr-bg-[radial-gradient(theme(textColor.black)_1px,_transparent_0)] shr-bg-[length:16px_16px]\"\n ref={ref}\n >\n <div className=\"shr-sticky shr-start-0 shr-top-0 shr-z-[1] shr-flex shr-w-full shr-flex-shrink-0 shr-gap-0.5\">\n <Controller\n scale={scale}\n setScale={setScale}\n scaleSteps={scaleSteps || defaultScaleSteps}\n onClickScaleUpButton={scaleUp}\n onClickScaleDownButton={scaleDown}\n onClickRotateButton={rotate}\n />\n </div>\n <div className=\"shr-z-[0] shr-mx-auto shr-my-0 shr-box-border shr-flex shr-w-fit shr-flex-shrink-0 shr-grow shr-items-center shr-justify-center shr-px-2 shr-pb-2\">\n {!loaded && (\n <div className=\"shr-pointer-events-none shr-fixed shr-inset-0 shr-flex shr-h-full shr-w-full shr-items-center shr-justify-center\">\n <Loader type=\"light\" size=\"m\" />\n </div>\n )}\n <div className={!loaded ? 'shr-invisible' : ''}>\n {file.contentType === 'application/pdf' ? (\n <PDFViewer\n scale={scale}\n rotation={rotation}\n file={file}\n width={width}\n onLoad={handleLoaded}\n onPassword={onPassword}\n />\n ) : file.contentType.startsWith('image/') ? (\n <ImageViewer\n scale={scale}\n rotation={rotation}\n file={file}\n width={width}\n onLoad={handleLoaded}\n />\n ) : (\n <Text>\n <Localizer\n id=\"smarthr-ui/FileViewer/unsupportedFileText\"\n defaultText=\"サポートされていない形式のファイルです。\"\n />\n </Text>\n )}\n </div>\n </div>\n </div>\n )\n}\n\ntype ControllerProps = {\n scale: number\n setScale: (scale: number) => void\n scaleSteps: number[]\n onClickScaleUpButton: () => void\n onClickScaleDownButton: () => void\n onClickRotateButton: () => void\n}\n\nconst Controller: FC<ControllerProps> = memo(\n ({\n scale,\n setScale,\n scaleSteps,\n onClickScaleUpButton,\n onClickScaleDownButton,\n onClickRotateButton,\n }) => (\n <div className=\"shr-sticky shr-flex shr-w-full shr-items-center shr-justify-center shr-justify-items-center shr-gap-0.5 shr-bg-scrim shr-p-0.5 shr-shadow-layer-1\">\n <Cluster gap={0.5}>\n <div className=\"shr-border-shorthand shr-flex shr-divide-x shr-divide-solid shr-overflow-hidden shr-rounded-m\">\n <Button\n onClick={onClickScaleDownButton}\n disabled={scale <= scaleSteps[0]}\n className=\"shr-rounded-none shr-border-none\"\n >\n <FaMagnifyingGlassMinusIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/scaleDownAlt\" defaultText=\"縮小\" />}\n />\n </Button>\n <DropdownMenuButton\n label={\n <Text>\n <VisuallyHiddenText>\n <Localizer id=\"smarthr-ui/FileViewer/scaleRateLabel\" defaultText=\"拡大率\" />\n </VisuallyHiddenText>\n {`${(scale * 100).toFixed(0)}%`}\n </Text>\n }\n className=\"shr-border-y-0 shr-border-[theme(borderColor.default)] [&_.smarthr-ui-Button]:shr-rounded-none [&_.smarthr-ui-Button]:shr-border-[transparent]\"\n >\n {scaleSteps.map((step) => (\n <Button\n key={step.toString()}\n onClick={() => setScale(step)}\n className=\"shr-rounded-none shr-border-0\"\n >\n {`${(step * 100).toFixed(0)}%`}\n </Button>\n ))}\n </DropdownMenuButton>\n <Button onClick={onClickScaleUpButton} className=\"shr-rounded-none shr-border-0\">\n <FaMagnifyingGlassPlusIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/scaleUpAlt\" defaultText=\"拡大\" />}\n />\n </Button>\n </div>\n <Button onClick={onClickRotateButton} className=\"shr-p-0.75\">\n <FaArrowRotateLeftIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/rotateAlt\" defaultText=\"左回転\" />}\n />\n </Button>\n </Cluster>\n </div>\n ),\n)\n"]}
1
+ {"version":3,"file":"FileViewer.js","sourceRoot":"","sources":["../../../src/components/FileViewer/FileViewer.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport Decimal from 'decimal.js'\nimport {\n type ComponentProps,\n type FC,\n memo,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n Button,\n Cluster,\n DropdownMenuButton,\n FaArrowRotateLeftIcon,\n FaMagnifyingGlassMinusIcon,\n FaMagnifyingGlassPlusIcon,\n Loader,\n Text,\n VisuallyHiddenText,\n} from '../..'\nimport { Localizer } from '../../intl'\n\nimport { ImageViewer } from './ImageViewer'\nimport { PDFViewer } from './PDFViewer'\n\nimport type { FileForViewer } from './types'\n\nconst defaultScaleStep = new Decimal(0.2)\nconst defaultScaleSteps = [0.2, 0.6, 1, 1.6, 2, 3]\n\ntype Props = {\n file: FileForViewer\n width?: number\n\n /*\n * 拡大縮小率のステップを、100%を1とした配列で指定します。\n * */\n scaleSteps?: number[]\n\n scaleStep?: number\n onPassword?: ComponentProps<typeof PDFViewer>['onPassword']\n onLoadError?: () => void\n}\n\nexport const FileViewer: FC<Props> = ({\n file,\n scaleStep,\n scaleSteps,\n width: fixedWidth,\n onPassword,\n onLoadError,\n}) => {\n const ref = useRef<HTMLDivElement>(null)\n const [scale, setScale] = useState(1)\n const [loaded, setLoaded] = useState(false)\n const [rotation, setRotation] = useState(0)\n const [width, setWidth] = useState(fixedWidth ?? 0)\n\n const internalScaleStep = useMemo(\n () => (scaleStep ? new Decimal(scaleStep) : defaultScaleStep),\n [scaleStep],\n )\n\n const scaleUp = useCallback(() => {\n setScale((currentScale) => new Decimal(currentScale).add(internalScaleStep).toNumber())\n }, [internalScaleStep])\n\n const scaleDown = useCallback(() => {\n setScale((currentScale) => new Decimal(currentScale).sub(internalScaleStep).toNumber())\n }, [internalScaleStep])\n\n const rotate = useCallback(() => {\n setRotation((currentRotation) => currentRotation - 90)\n }, [])\n\n const handleLoaded = useCallback(() => {\n setLoaded(true)\n }, [])\n\n useEffect(() => {\n if (!ref.current || fixedWidth !== undefined) {\n return\n }\n\n const resizeObserver = new ResizeObserver(() => {\n setWidth((ref.current?.clientWidth ?? 0) - 64)\n })\n\n resizeObserver.observe(ref.current)\n\n return () => {\n resizeObserver.disconnect()\n }\n }, [fixedWidth])\n\n return (\n <div\n className=\"shr-flex shr-h-full shr-w-full shr-flex-col shr-gap-2 shr-overflow-auto shr-bg-scrim shr-bg-[radial-gradient(theme(textColor.black)_1px,_transparent_0)] shr-bg-[length:16px_16px]\"\n ref={ref}\n >\n <div className=\"shr-sticky shr-start-0 shr-top-0 shr-z-[1] shr-flex shr-w-full shr-flex-shrink-0 shr-gap-0.5\">\n <Controller\n scale={scale}\n setScale={setScale}\n scaleSteps={scaleSteps || defaultScaleSteps}\n onClickScaleUpButton={scaleUp}\n onClickScaleDownButton={scaleDown}\n onClickRotateButton={rotate}\n />\n </div>\n <div className=\"shr-z-[0] shr-mx-auto shr-my-0 shr-box-border shr-flex shr-w-fit shr-flex-shrink-0 shr-grow shr-items-center shr-justify-center shr-px-2 shr-pb-2\">\n {!loaded && (\n <div className=\"shr-pointer-events-none shr-fixed shr-inset-0 shr-flex shr-h-full shr-w-full shr-items-center shr-justify-center\">\n <Loader type=\"light\" size=\"m\" />\n </div>\n )}\n <div className={!loaded ? 'shr-invisible' : ''}>\n {file.contentType === 'application/pdf' ? (\n <PDFViewer\n scale={scale}\n rotation={rotation}\n file={file}\n width={width}\n onLoad={handleLoaded}\n onPassword={onPassword}\n onLoadError={onLoadError}\n />\n ) : file.contentType.startsWith('image/') ? (\n <ImageViewer\n scale={scale}\n rotation={rotation}\n file={file}\n width={width}\n onLoad={handleLoaded}\n onLoadError={onLoadError}\n />\n ) : (\n <Text>\n <Localizer\n id=\"smarthr-ui/FileViewer/unsupportedFileText\"\n defaultText=\"サポートされていない形式のファイルです。\"\n />\n </Text>\n )}\n </div>\n </div>\n </div>\n )\n}\n\ntype ControllerProps = {\n scale: number\n setScale: (scale: number) => void\n scaleSteps: number[]\n onClickScaleUpButton: () => void\n onClickScaleDownButton: () => void\n onClickRotateButton: () => void\n}\n\nconst Controller: FC<ControllerProps> = memo(\n ({\n scale,\n setScale,\n scaleSteps,\n onClickScaleUpButton,\n onClickScaleDownButton,\n onClickRotateButton,\n }) => (\n <div className=\"shr-sticky shr-flex shr-w-full shr-items-center shr-justify-center shr-justify-items-center shr-gap-0.5 shr-bg-scrim shr-p-0.5 shr-shadow-layer-1\">\n <Cluster gap={0.5}>\n <div className=\"shr-border-shorthand shr-flex shr-divide-x shr-divide-solid shr-overflow-hidden shr-rounded-m\">\n <Button\n onClick={onClickScaleDownButton}\n disabled={scale <= scaleSteps[0]}\n className=\"shr-rounded-none shr-border-none\"\n >\n <FaMagnifyingGlassMinusIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/scaleDownAlt\" defaultText=\"縮小\" />}\n />\n </Button>\n <DropdownMenuButton\n label={\n <Text>\n <VisuallyHiddenText>\n <Localizer id=\"smarthr-ui/FileViewer/scaleRateLabel\" defaultText=\"拡大率\" />\n </VisuallyHiddenText>\n {`${(scale * 100).toFixed(0)}%`}\n </Text>\n }\n className=\"shr-border-y-0 shr-border-[theme(borderColor.default)] [&_.smarthr-ui-Button]:shr-rounded-none [&_.smarthr-ui-Button]:shr-border-[transparent]\"\n >\n {scaleSteps.map((step) => (\n <Button\n key={step.toString()}\n onClick={() => setScale(step)}\n className=\"shr-rounded-none shr-border-0\"\n >\n {`${(step * 100).toFixed(0)}%`}\n </Button>\n ))}\n </DropdownMenuButton>\n <Button onClick={onClickScaleUpButton} className=\"shr-rounded-none shr-border-0\">\n <FaMagnifyingGlassPlusIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/scaleUpAlt\" defaultText=\"拡大\" />}\n />\n </Button>\n </div>\n <Button onClick={onClickRotateButton} className=\"shr-p-0.75\">\n <FaArrowRotateLeftIcon\n alt={<Localizer id=\"smarthr-ui/FileViewer/rotateAlt\" defaultText=\"左回転\" />}\n />\n </Button>\n </Cluster>\n </div>\n ),\n)\n"]}
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.ImageViewer = void 0;
5
5
  const jsx_runtime_1 = require("react/jsx-runtime");
6
6
  const react_1 = require("react");
7
- exports.ImageViewer = (0, react_1.memo)(({ scale, rotation, file, width, onLoad }) => {
7
+ exports.ImageViewer = (0, react_1.memo)(({ scale, rotation, file, width, onLoad, onLoadError }) => {
8
8
  const imageRef = (0, react_1.useRef)(null);
9
9
  const [viewConfig, setViewConfig] = (0, react_1.useState)({
10
10
  wrapperWidth: 0,
@@ -44,6 +44,6 @@ exports.ImageViewer = (0, react_1.memo)(({ scale, rotation, file, width, onLoad
44
44
  }, className: "shr-relative shr-h-full shr-w-full", children: (0, jsx_runtime_1.jsx)("img", { className: "shr-absolute shr-left-[50%] shr-top-[50%] shr-origin-top-left -shr-translate-x-1/2 -shr-translate-y-1/2", ref: imageRef, src: file.url, alt: file.alt, style: {
45
45
  rotate: `${rotation}deg`,
46
46
  scale: `${viewConfig.imgScale}`,
47
- }, onLoad: handleLoad }) }));
47
+ }, onLoad: handleLoad, onError: onLoadError }) }));
48
48
  });
49
49
  //# sourceMappingURL=ImageViewer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ImageViewer.js","sourceRoot":"","sources":["../../../src/components/FileViewer/ImageViewer.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAA;;;;AAEZ,iCAA+E;AAIlE,QAAA,WAAW,GAAoB,IAAA,YAAI,EAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;IAC5F,MAAM,QAAQ,GAAG,IAAA,cAAM,EAAmB,IAAI,CAAC,CAAA;IAC/C,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,IAAA,gBAAQ,EAAC;QAC3C,YAAY,EAAE,CAAC;QACf,aAAa,EAAE,CAAC;QAChB,QAAQ,EAAE,CAAC;KACZ,CAAC,CAAA;IAEF,0DAA0D;IAC1D,MAAM,gBAAgB,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QACxC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;YAChC,OAAM;QACR,CAAC;QAED,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAA;QAC5B,4BAA4B;QAC5B,MAAM,aAAa,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,KAAK,CAAA;QAExD,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAA;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QAEnC,iCAAiC;QACjC,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,GAAG,aAAa,CAAA;QACpD,MAAM,YAAY,GAAG,GAAG,CAAC,aAAa,GAAG,aAAa,CAAA;QAEtD,aAAa,CAAC;YACZ,YAAY,EAAE,WAAW,GAAG,GAAG,GAAG,YAAY,GAAG,GAAG;YACpD,aAAa,EAAE,WAAW,GAAG,GAAG,GAAG,YAAY,GAAG,GAAG;YACrD,QAAQ,EAAE,aAAa;SACxB,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAA;IAE5B,MAAM,UAAU,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QAClC,gBAAgB,EAAE,CAAA;QAClB,MAAM,EAAE,EAAE,CAAA;IACZ,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAA;IAE9B,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,gBAAgB,EAAE,CAAA;IACpB,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAA;IAEtB,OAAO,CACL,gCACE,KAAK,EAAE;YACL,KAAK,EAAE,UAAU,CAAC,YAAY;YAC9B,MAAM,EAAE,UAAU,CAAC,aAAa;SACjC,EACD,SAAS,EAAC,oCAAoC,YAI9C,gCACE,SAAS,EAAC,yGAAyG,EACnH,GAAG,EAAE,QAAQ,EACb,GAAG,EAAE,IAAI,CAAC,GAAG,EACb,GAAG,EAAE,IAAI,CAAC,GAAG,EACb,KAAK,EAAE;gBACL,MAAM,EAAE,GAAG,QAAQ,KAAK;gBACxB,KAAK,EAAE,GAAG,UAAU,CAAC,QAAQ,EAAE;aAChC,EACD,MAAM,EAAE,UAAU,GAClB,GACE,CACP,CAAA;AACH,CAAC,CAAC,CAAA","sourcesContent":["'use client'\n\nimport { type FC, memo, useCallback, useEffect, useRef, useState } from 'react'\n\nimport type { ViewerProps } from './types'\n\nexport const ImageViewer: FC<ViewerProps> = memo(({ scale, rotation, file, width, onLoad }) => {\n const imageRef = useRef<HTMLImageElement>(null)\n const [viewConfig, setViewConfig] = useState({\n wrapperWidth: 0,\n wrapperHeight: 0,\n imgScale: 1,\n })\n\n // CSSのみではscale, transformの値を親に適用してスクロールするようにできないため、計算している\n const updateViewConfig = useCallback(() => {\n if (!imageRef.current?.complete) {\n return\n }\n\n const img = imageRef.current\n // 与えられたwidthに対する適切なscaleを算出\n const viewportScale = (width / img.naturalWidth) * scale\n\n const rad = (rotation * Math.PI) / 180\n const sin = Math.abs(Math.sin(rad))\n const cos = Math.abs(Math.cos(rad))\n\n // imgをwidth: 100%で表示したときと同等の値を算出\n const scaledWidth = img.naturalWidth * viewportScale\n const scaledHeight = img.naturalHeight * viewportScale\n\n setViewConfig({\n wrapperWidth: scaledWidth * cos + scaledHeight * sin,\n wrapperHeight: scaledWidth * sin + scaledHeight * cos,\n imgScale: viewportScale,\n })\n }, [scale, rotation, width])\n\n const handleLoad = useCallback(() => {\n updateViewConfig()\n onLoad?.()\n }, [updateViewConfig, onLoad])\n\n useEffect(() => {\n updateViewConfig()\n }, [updateViewConfig])\n\n return (\n <div\n style={{\n width: viewConfig.wrapperWidth,\n height: viewConfig.wrapperHeight,\n }}\n className=\"shr-relative shr-h-full shr-w-full\"\n >\n {/* imgのload完了時にupdateViewConfigを呼び出さないと適切なサイズが取得できないため */}\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}\n <img\n className=\"shr-absolute shr-left-[50%] shr-top-[50%] shr-origin-top-left -shr-translate-x-1/2 -shr-translate-y-1/2\"\n ref={imageRef}\n src={file.url}\n alt={file.alt}\n style={{\n rotate: `${rotation}deg`,\n scale: `${viewConfig.imgScale}`,\n }}\n onLoad={handleLoad}\n />\n </div>\n )\n})\n"]}
1
+ {"version":3,"file":"ImageViewer.js","sourceRoot":"","sources":["../../../src/components/FileViewer/ImageViewer.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAA;;;;AAEZ,iCAA+E;AAIlE,QAAA,WAAW,GAAoB,IAAA,YAAI,EAC9C,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE;IACxD,MAAM,QAAQ,GAAG,IAAA,cAAM,EAAmB,IAAI,CAAC,CAAA;IAC/C,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,IAAA,gBAAQ,EAAC;QAC3C,YAAY,EAAE,CAAC;QACf,aAAa,EAAE,CAAC;QAChB,QAAQ,EAAE,CAAC;KACZ,CAAC,CAAA;IAEF,0DAA0D;IAC1D,MAAM,gBAAgB,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QACxC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;YAChC,OAAM;QACR,CAAC;QAED,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAA;QAC5B,4BAA4B;QAC5B,MAAM,aAAa,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,KAAK,CAAA;QAExD,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAA;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QAEnC,iCAAiC;QACjC,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,GAAG,aAAa,CAAA;QACpD,MAAM,YAAY,GAAG,GAAG,CAAC,aAAa,GAAG,aAAa,CAAA;QAEtD,aAAa,CAAC;YACZ,YAAY,EAAE,WAAW,GAAG,GAAG,GAAG,YAAY,GAAG,GAAG;YACpD,aAAa,EAAE,WAAW,GAAG,GAAG,GAAG,YAAY,GAAG,GAAG;YACrD,QAAQ,EAAE,aAAa;SACxB,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAA;IAE5B,MAAM,UAAU,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QAClC,gBAAgB,EAAE,CAAA;QAClB,MAAM,EAAE,EAAE,CAAA;IACZ,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAA;IAE9B,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,gBAAgB,EAAE,CAAA;IACpB,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAA;IAEtB,OAAO,CACL,gCACE,KAAK,EAAE;YACL,KAAK,EAAE,UAAU,CAAC,YAAY;YAC9B,MAAM,EAAE,UAAU,CAAC,aAAa;SACjC,EACD,SAAS,EAAC,oCAAoC,YAI9C,gCACE,SAAS,EAAC,yGAAyG,EACnH,GAAG,EAAE,QAAQ,EACb,GAAG,EAAE,IAAI,CAAC,GAAG,EACb,GAAG,EAAE,IAAI,CAAC,GAAG,EACb,KAAK,EAAE;gBACL,MAAM,EAAE,GAAG,QAAQ,KAAK;gBACxB,KAAK,EAAE,GAAG,UAAU,CAAC,QAAQ,EAAE;aAChC,EACD,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,GACpB,GACE,CACP,CAAA;AACH,CAAC,CACF,CAAA","sourcesContent":["'use client'\n\nimport { type FC, memo, useCallback, useEffect, useRef, useState } from 'react'\n\nimport type { ViewerProps } from './types'\n\nexport const ImageViewer: FC<ViewerProps> = memo(\n ({ scale, rotation, file, width, onLoad, onLoadError }) => {\n const imageRef = useRef<HTMLImageElement>(null)\n const [viewConfig, setViewConfig] = useState({\n wrapperWidth: 0,\n wrapperHeight: 0,\n imgScale: 1,\n })\n\n // CSSのみではscale, transformの値を親に適用してスクロールするようにできないため、計算している\n const updateViewConfig = useCallback(() => {\n if (!imageRef.current?.complete) {\n return\n }\n\n const img = imageRef.current\n // 与えられたwidthに対する適切なscaleを算出\n const viewportScale = (width / img.naturalWidth) * scale\n\n const rad = (rotation * Math.PI) / 180\n const sin = Math.abs(Math.sin(rad))\n const cos = Math.abs(Math.cos(rad))\n\n // imgをwidth: 100%で表示したときと同等の値を算出\n const scaledWidth = img.naturalWidth * viewportScale\n const scaledHeight = img.naturalHeight * viewportScale\n\n setViewConfig({\n wrapperWidth: scaledWidth * cos + scaledHeight * sin,\n wrapperHeight: scaledWidth * sin + scaledHeight * cos,\n imgScale: viewportScale,\n })\n }, [scale, rotation, width])\n\n const handleLoad = useCallback(() => {\n updateViewConfig()\n onLoad?.()\n }, [updateViewConfig, onLoad])\n\n useEffect(() => {\n updateViewConfig()\n }, [updateViewConfig])\n\n return (\n <div\n style={{\n width: viewConfig.wrapperWidth,\n height: viewConfig.wrapperHeight,\n }}\n className=\"shr-relative shr-h-full shr-w-full\"\n >\n {/* imgのload完了時にupdateViewConfigを呼び出さないと適切なサイズが取得できないため */}\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}\n <img\n className=\"shr-absolute shr-left-[50%] shr-top-[50%] shr-origin-top-left -shr-translate-x-1/2 -shr-translate-y-1/2\"\n ref={imageRef}\n src={file.url}\n alt={file.alt}\n style={{\n rotate: `${rotation}deg`,\n scale: `${viewConfig.imgScale}`,\n }}\n onLoad={handleLoad}\n onError={onLoadError}\n />\n </div>\n )\n },\n)\n"]}
@@ -38,7 +38,7 @@ const options = {
38
38
  // cMapUrl: '/cmaps/',
39
39
  cMapUrl: `//unpkg.com/pdfjs-dist@${react_pdf_1.pdfjs.version}/cmaps/`,
40
40
  };
41
- exports.PDFViewer = (0, react_1.memo)(({ scale, rotation, file, width, onLoad, onPassword }) => {
41
+ exports.PDFViewer = (0, react_1.memo)(({ scale, rotation, file, width, onLoad, onPassword, onLoadError }) => {
42
42
  const [pdfNumPages, setPdfNumPages] = (0, react_1.useState)(1);
43
43
  const onDocumentLoadSuccess = (0, react_1.useCallback)(({ numPages }) => {
44
44
  setPdfNumPages(numPages);
@@ -55,6 +55,6 @@ exports.PDFViewer = (0, react_1.memo)(({ scale, rotation, file, width, onLoad, o
55
55
  onLoad();
56
56
  };
57
57
  }, [pdfNumPages, onLoad]);
58
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(generatedReactPDFStyle_1.ReactPDFStyle, {}), (0, jsx_runtime_1.jsx)(react_pdf_1.Document, { options: options, file: file.url, onLoadSuccess: onDocumentLoadSuccess, rotate: rotation, className: "shr-flex shr-h-full shr-w-fit shr-flex-col shr-items-center shr-gap-1 shr-overflow-auto", externalLinkTarget: "_blank", loading: null, onPassword: onPassword, children: Array.from({ length: pdfNumPages }).map((_, i) => ((0, jsx_runtime_1.jsx)(react_pdf_1.Page, { pageNumber: i + 1, width: width, scale: scale, className: "shr-w-full", onLoadSuccess: onPageLoad, loading: null }, `page_${i}`))) })] }));
58
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(generatedReactPDFStyle_1.ReactPDFStyle, {}), (0, jsx_runtime_1.jsx)(react_pdf_1.Document, { options: options, file: file.url, onLoadSuccess: onDocumentLoadSuccess, onLoadError: onLoadError, rotate: rotation, className: "shr-flex shr-h-full shr-w-fit shr-flex-col shr-items-center shr-gap-1 shr-overflow-auto", externalLinkTarget: "_blank", loading: null, onPassword: onPassword, children: Array.from({ length: pdfNumPages }).map((_, i) => ((0, jsx_runtime_1.jsx)(react_pdf_1.Page, { pageNumber: i + 1, width: width, scale: scale, className: "shr-w-full", onLoadSuccess: onPageLoad, loading: null }, `page_${i}`))) })] }));
59
59
  });
60
60
  //# sourceMappingURL=PDFViewer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PDFViewer.js","sourceRoot":"","sources":["../../../src/components/FileViewer/PDFViewer.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { type ComponentProps, type FC, memo, useCallback, useMemo, useState } from 'react'\nimport { Document, Page, pdfjs } from 'react-pdf'\n\nimport { ReactPDFStyle } from './generatedReactPDFStyle'\n\nimport type { ViewerProps } from './types'\n\nif (typeof window !== 'undefined') {\n // iOS 17.3以下ではPromise.withResolversが未定義のため、polyfillを適用する\n // @ts-expect-error\n if (typeof window.Promise.withResolvers === 'undefined') {\n // @ts-expect-error\n window.Promise.withResolvers = function () {\n let resolve, reject\n const promise = new Promise((res, rej) => {\n resolve = res\n reject = rej\n })\n return { promise, resolve, reject }\n }\n // web workerもpolyfillされたものを読み込む\n pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`\n } else {\n // TODO: バンドラの関係でCDNから読み込んでいるが、smarthr-uiから配信するようにしたい\n // pdfjs.GlobalWorkerOptions.workerSrc = new URL(\n // 'pdfjs-dist/build/pdf.worker.min.mjs',\n // import.meta.url,\n // ).toString()\n pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`\n }\n}\n\nconst options = {\n // TODO: バンドラの関係でCDNから読み込んでいるが、smarthr-uiから配信するようにしたい\n // 非latin文字を読み込むためのオプション\n // 参考: https://github.com/wojtekmaj/react-pdf?tab=readme-ov-file#support-for-non-latin-characters\n // cMapUrl: '/cmaps/',\n cMapUrl: `//unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/`,\n} satisfies ComponentProps<typeof Document>['options']\n\nexport const PDFViewer: FC<ViewerProps> = memo(\n ({ scale, rotation, file, width, onLoad, onPassword }) => {\n const [pdfNumPages, setPdfNumPages] = useState(1)\n\n const onDocumentLoadSuccess = useCallback<\n NonNullable<ComponentProps<typeof Document>['onLoadSuccess']>\n >(({ numPages }) => {\n setPdfNumPages(numPages)\n }, [])\n\n const onPageLoad: ComponentProps<typeof Page>['onLoadSuccess'] = useMemo(() => {\n if (!onLoad) {\n return undefined\n }\n\n return (page) => {\n // DocumentのLoadだとページごとの読み込みが考慮されないため\n if (page.pageNumber !== pdfNumPages) {\n return\n }\n onLoad()\n }\n }, [pdfNumPages, onLoad])\n\n return (\n <>\n {/* TODO: 外部CSSをsmarthr-uiから読み込んでもらえるようにする機構ができたら消す */}\n <ReactPDFStyle />\n <Document\n options={options}\n file={file.url}\n onLoadSuccess={onDocumentLoadSuccess}\n rotate={rotation}\n className=\"shr-flex shr-h-full shr-w-fit shr-flex-col shr-items-center shr-gap-1 shr-overflow-auto\"\n externalLinkTarget=\"_blank\"\n loading={null}\n onPassword={onPassword}\n >\n {Array.from({ length: pdfNumPages }).map((_, i) => (\n <Page\n key={`page_${i}`}\n pageNumber={i + 1}\n width={width}\n scale={scale}\n className=\"shr-w-full\"\n onLoadSuccess={onPageLoad}\n loading={null}\n />\n ))}\n </Document>\n </>\n )\n },\n)\n"]}
1
+ {"version":3,"file":"PDFViewer.js","sourceRoot":"","sources":["../../../src/components/FileViewer/PDFViewer.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { type ComponentProps, type FC, memo, useCallback, useMemo, useState } from 'react'\nimport { Document, Page, pdfjs } from 'react-pdf'\n\nimport { ReactPDFStyle } from './generatedReactPDFStyle'\n\nimport type { ViewerProps } from './types'\n\nif (typeof window !== 'undefined') {\n // iOS 17.3以下ではPromise.withResolversが未定義のため、polyfillを適用する\n // @ts-expect-error\n if (typeof window.Promise.withResolvers === 'undefined') {\n // @ts-expect-error\n window.Promise.withResolvers = function () {\n let resolve, reject\n const promise = new Promise((res, rej) => {\n resolve = res\n reject = rej\n })\n return { promise, resolve, reject }\n }\n // web workerもpolyfillされたものを読み込む\n pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`\n } else {\n // TODO: バンドラの関係でCDNから読み込んでいるが、smarthr-uiから配信するようにしたい\n // pdfjs.GlobalWorkerOptions.workerSrc = new URL(\n // 'pdfjs-dist/build/pdf.worker.min.mjs',\n // import.meta.url,\n // ).toString()\n pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`\n }\n}\n\nconst options = {\n // TODO: バンドラの関係でCDNから読み込んでいるが、smarthr-uiから配信するようにしたい\n // 非latin文字を読み込むためのオプション\n // 参考: https://github.com/wojtekmaj/react-pdf?tab=readme-ov-file#support-for-non-latin-characters\n // cMapUrl: '/cmaps/',\n cMapUrl: `//unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/`,\n} satisfies ComponentProps<typeof Document>['options']\n\nexport const PDFViewer: FC<ViewerProps> = memo(\n ({ scale, rotation, file, width, onLoad, onPassword, onLoadError }) => {\n const [pdfNumPages, setPdfNumPages] = useState(1)\n\n const onDocumentLoadSuccess = useCallback<\n NonNullable<ComponentProps<typeof Document>['onLoadSuccess']>\n >(({ numPages }) => {\n setPdfNumPages(numPages)\n }, [])\n\n const onPageLoad: ComponentProps<typeof Page>['onLoadSuccess'] = useMemo(() => {\n if (!onLoad) {\n return undefined\n }\n\n return (page) => {\n // DocumentのLoadだとページごとの読み込みが考慮されないため\n if (page.pageNumber !== pdfNumPages) {\n return\n }\n onLoad()\n }\n }, [pdfNumPages, onLoad])\n\n return (\n <>\n {/* TODO: 外部CSSをsmarthr-uiから読み込んでもらえるようにする機構ができたら消す */}\n <ReactPDFStyle />\n <Document\n options={options}\n file={file.url}\n onLoadSuccess={onDocumentLoadSuccess}\n onLoadError={onLoadError}\n rotate={rotation}\n className=\"shr-flex shr-h-full shr-w-fit shr-flex-col shr-items-center shr-gap-1 shr-overflow-auto\"\n externalLinkTarget=\"_blank\"\n loading={null}\n onPassword={onPassword}\n >\n {Array.from({ length: pdfNumPages }).map((_, i) => (\n <Page\n key={`page_${i}`}\n pageNumber={i + 1}\n width={width}\n scale={scale}\n className=\"shr-w-full\"\n onLoadSuccess={onPageLoad}\n loading={null}\n />\n ))}\n </Document>\n </>\n )\n },\n)\n"]}
@@ -15,4 +15,5 @@ export type ViewerProps = {
15
15
  * PDFファイルのパスワード入力を要求されたときに呼ばれるコールバック関数。PdfViewerでのみ使用されます。
16
16
  */
17
17
  onPassword?: ComponentProps<typeof Document>['onPassword'];
18
+ onLoadError?: () => void;
18
19
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/components/FileViewer/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ComponentProps } from 'react'\nimport type { Document } from 'react-pdf'\n\nexport type FileForViewer = {\n url: string\n contentType: string\n alt?: string\n}\n\nexport type ViewerProps = {\n file: FileForViewer\n scale: number\n rotation: number\n width: number\n onLoad: () => void\n /**\n * PDFファイルのパスワード入力を要求されたときに呼ばれるコールバック関数。PdfViewerでのみ使用されます。\n */\n onPassword?: ComponentProps<typeof Document>['onPassword']\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/components/FileViewer/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ComponentProps } from 'react'\nimport type { Document } from 'react-pdf'\n\nexport type FileForViewer = {\n url: string\n contentType: string\n alt?: string\n}\n\nexport type ViewerProps = {\n file: FileForViewer\n scale: number\n rotation: number\n width: number\n onLoad: () => void\n /**\n * PDFファイルのパスワード入力を要求されたときに呼ばれるコールバック関数。PdfViewerでのみ使用されます。\n */\n onPassword?: ComponentProps<typeof Document>['onPassword']\n onLoadError?: () => void\n}\n"]}
@@ -192,6 +192,35 @@ const ActualFormControl = ({ title, titleType = 'blockTitle', subActionArea, dan
192
192
  }
193
193
  }
194
194
  }, [actualErrorMessages.length, autoBindErrorInput]);
195
+ // HINT: Fieldset内の可視ラベルが無いinputに、legend文言をアクセシブルネームに追加する
196
+ // https://waic.jp/translations/WCAG21/Understanding/label-in-name.html
197
+ (0, react_2.useEffect)(() => {
198
+ if (!isFieldset || !inputWrapperRef.current)
199
+ return;
200
+ const inputs = inputWrapperRef.current.querySelectorAll(SMARTHR_UI_INPUT_SELECTOR);
201
+ if (!inputs.length)
202
+ return;
203
+ const legendText = (0, react_innertext_1.default)(title);
204
+ if (!legendText)
205
+ return;
206
+ const labels = new Set(Array.from(document.querySelectorAll('label[for]')).map((label) => label.getAttribute('for')));
207
+ inputs.forEach((input) => {
208
+ const inputId = input.getAttribute('id');
209
+ if (inputId && labels.has(inputId)) {
210
+ return;
211
+ }
212
+ let accessibleName = '';
213
+ if (input.hasAttribute('aria-label')) {
214
+ accessibleName = input.getAttribute('aria-label') || '';
215
+ }
216
+ else if (input.hasAttribute('title')) {
217
+ accessibleName = input.getAttribute('title') || '';
218
+ }
219
+ if (!accessibleName.includes(legendText)) {
220
+ input.setAttribute('aria-label', `${legendText} ${accessibleName}`.trim());
221
+ }
222
+ });
223
+ }, [isFieldset, title]);
195
224
  let body = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(HelpMessageParagraph, { helpMessage: helpMessage, managedHtmlFor: managedHtmlFor }), (0, jsx_runtime_1.jsx)(ExampleMessageText, { exampleMessage: exampleMessage, managedHtmlFor: managedHtmlFor }), (0, jsx_runtime_1.jsx)(ErrorMessageList, { errorMessages: actualErrorMessages, managedHtmlFor: managedHtmlFor, classNames: classNames }), (0, jsx_runtime_1.jsx)("div", { className: classNames.childrenWrapper, ref: inputWrapperRef, children: children }), (0, jsx_runtime_1.jsx)(SupplementaryMessageText, { supplementaryMessage: supplementaryMessage, managedHtmlFor: managedHtmlFor })] }));
196
225
  // HINT: dangerouslyTitleHiddenの場合、body以下の余白の計算を簡略化するため
197
226
  // Stackをネストし、そのStackに対してmargin-top: 0を指定する
@@ -1 +1 @@
1
- {"version":3,"file":"FormControl.js","sourceRoot":"","sources":["../../../src/components/FormControl/FormControl.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type ComponentType,\n type FC,\n Fragment,\n type FunctionComponentElement,\n type PropsWithChildren,\n type ReactNode,\n memo,\n useEffect,\n useMemo,\n useRef,\n} from 'react'\nimport { useId } from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { FaCircleExclamationIcon } from '../Icon'\nimport { Cluster, Stack } from '../Layout'\nimport { StatusLabel } from '../StatusLabel'\nimport { Text, type TextProps } from '../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassNameGenerator } from '../VisuallyHiddenText'\n\nimport type { Gap } from '../../types'\n\ntype StatusLabelProps = ComponentProps<typeof StatusLabel>\ntype StatusLabelType = FunctionComponentElement<StatusLabelProps>\n\ntype Props = PropsWithChildren<{\n /** グループのタイトル名 */\n title: ReactNode\n /** タイトルの見出しのタイプ */\n titleType?: TextProps['styleType']\n /** タイトル右の領域 */\n subActionArea?: ReactNode\n /** タイトルの見出しを視覚的に隠すかどうか */\n dangerouslyTitleHidden?: boolean\n /** label 要素に適用する `htmlFor` 値 */\n htmlFor?: string\n /** label 要素に適用する `id` 値 */\n labelId?: string\n /** タイトル群と子要素の間の間隔調整用(基本的には不要) */\n innerMargin?: Gap\n /** タイトルの隣に表示する `StatusLabel` の Props の配列 */\n /**\n * @deprecated statusLabelProps属性は非推奨です。statusLabelsを使ってください。\n */\n statusLabelProps?: StatusLabelProps | StatusLabelProps[]\n /** タイトルの隣に表示する `StatusLabel` の配列 */\n statusLabels?: StatusLabelType | StatusLabelType[]\n /** タイトルの下に表示するヘルプメッセージ */\n helpMessage?: ReactNode\n /** タイトルの下に表示する入力例 */\n exampleMessage?: ReactNode\n /** タイトルの下に表示するエラーメッセージ */\n errorMessages?: ReactNode | ReactNode[]\n /** エラーがある場合に自動的に入力要素を error にするかどうか */\n autoBindErrorInput?: boolean\n /** フォームコントロールの下に表示する補足メッセージ */\n supplementaryMessage?: ReactNode\n /** `true` のとき、文字色を `TEXT_DISABLED` にする */\n disabled?: boolean\n as?: string | ComponentType<any>\n}>\ntype ElementProps = Omit<ComponentPropsWithoutRef<'div'>, keyof Props | 'aria-labelledby'>\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-FormControl',\n 'shr-mx-[unset] shr-border-none shr-p-[unset]',\n 'disabled:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-label_>_span]:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-exampleMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-errorMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-supplementaryMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-Input]:shr-border-default/50 [&:disabled_.smarthr-ui-Input]:shr-bg-white-darken',\n ],\n label: ['smarthr-ui-FormControl-label'],\n errorList: ['shr-list-none'],\n errorIcon: ['smarthr-ui-FormControl-errorMessage', 'shr-text-danger'],\n underTitleStack: ['[&&&]:shr-mt-0'],\n childrenWrapper: [],\n },\n variants: {\n innerMargin: {\n 0: {},\n 0.25: {},\n 0.5: {},\n 0.75: {},\n 1: {},\n 1.25: {},\n 1.5: {},\n 2: {},\n 2.5: {},\n 3: {},\n 3.5: {},\n 4: {},\n 8: {},\n X3S: {},\n XXS: {},\n XS: {},\n S: {},\n M: {},\n L: {},\n XL: {},\n XXL: {},\n X3L: {},\n } as { [key in Gap]: string },\n isFieldset: {\n true: {},\n false: {},\n },\n },\n compoundVariants: [\n // TODO: innerMarginが未指定、初期値の場合、かつFieldsetの場合、childrenの上部の余白を広げることで\n // FormControltとの差をわかりやすくしている\n // 微妙な方法ではあるので、必要に応じてinnerMarginではない属性を用意する\n // https://kufuinc.slack.com/archives/CGC58MW01/p1737944965871159?thread_ts=1737541173.404369&cid=CGC58MW01\n {\n innerMargin: undefined,\n isFieldset: true,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-1',\n },\n },\n {\n innerMargin: undefined,\n isFieldset: false,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-0.5',\n },\n },\n ],\n})\n\nconst SMARTHR_UI_INPUT_SELECTOR = '[data-smarthr-ui-input=\"true\"]'\n\nexport const ActualFormControl: FC<Props & ElementProps> = ({\n title,\n titleType = 'blockTitle',\n subActionArea,\n dangerouslyTitleHidden = false,\n htmlFor,\n labelId,\n innerMargin,\n statusLabels,\n statusLabelProps,\n helpMessage,\n exampleMessage,\n errorMessages,\n autoBindErrorInput = true,\n supplementaryMessage,\n as = 'div',\n className,\n children,\n ...props\n}) => {\n const defaultHtmlFor = useId()\n const defaultLabelId = useId()\n const managedHtmlFor = htmlFor || defaultHtmlFor\n const managedLabelId = labelId || defaultLabelId\n const inputWrapperRef = useRef<HTMLDivElement>(null)\n const isFieldset = as === 'fieldset'\n\n const describedbyIds = useMemo(() => {\n const temp = []\n\n if (helpMessage) {\n temp.push(`${managedHtmlFor}_helpMessage`)\n }\n if (exampleMessage) {\n temp.push(`${managedHtmlFor}_exampleMessage`)\n }\n if (supplementaryMessage) {\n temp.push(`${managedHtmlFor}_supplementaryMessage`)\n }\n if (errorMessages) {\n temp.push(`${managedHtmlFor}_errorMessages`)\n }\n\n return temp.join(' ')\n }, [helpMessage, exampleMessage, supplementaryMessage, errorMessages, managedHtmlFor])\n\n const actualStatusLabels = useMemo(() => {\n if (statusLabels) {\n return (Array.isArray(statusLabels) ? statusLabels : [statusLabels]).map(\n (statusLabel, index) => <Fragment key={index}>{statusLabel}</Fragment>,\n )\n }\n\n if (!statusLabelProps) {\n return []\n }\n\n const labelProps = Array.isArray(statusLabelProps) ? statusLabelProps : [statusLabelProps]\n\n return labelProps.map((prop, index) => <StatusLabel {...prop} key={index} />)\n }, [statusLabels, statusLabelProps])\n\n const actualErrorMessages = useMemo(() => {\n if (!errorMessages) {\n return []\n }\n\n return Array.isArray(errorMessages) ? errorMessages : [errorMessages]\n }, [errorMessages])\n\n const actualInnerMargin = useMemo(() => innerMargin ?? 0.5, [innerMargin])\n\n const classNames = useMemo(() => {\n const { wrapper, label, errorList, errorIcon, underTitleStack, childrenWrapper } =\n classNameGenerator({ innerMargin, isFieldset })\n\n return {\n wrapper: wrapper({ className }),\n label: label({\n className: dangerouslyTitleHidden ? visuallyHiddenTextClassNameGenerator() : '',\n }),\n errorList: errorList(),\n errorIcon: errorIcon(),\n underTitleStack: underTitleStack(),\n childrenWrapper: childrenWrapper(),\n }\n }, [innerMargin, isFieldset, dangerouslyTitleHidden, className])\n\n useEffect(() => {\n if (\n isFieldset ||\n !inputWrapperRef?.current ||\n // HINT: 対象idを持つ要素が既に存在する場合、何もしない\n document.getElementById(managedHtmlFor)\n ) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!input) {\n return\n }\n\n if (!input.getAttribute('id')) {\n input.setAttribute('id', managedHtmlFor)\n }\n\n if (input instanceof HTMLInputElement && input.type === 'file') {\n const attrName = 'aria-labelledby'\n const inputLabelledByIds = input.getAttribute(attrName)\n\n if (inputLabelledByIds) {\n // InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める\n input.setAttribute(attrName, `${inputLabelledByIds} ${managedLabelId}`)\n }\n }\n }, [managedHtmlFor, isFieldset, managedLabelId])\n\n useEffect(() => {\n if (!describedbyIds || !inputWrapperRef?.current) {\n return\n }\n\n const inputWrapper = inputWrapperRef.current\n const attrName = 'aria-describedby'\n\n if (inputWrapper.querySelector(`[${attrName}=\"${describedbyIds}\"]`)) {\n return\n }\n\n const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attribute = input.getAttribute(attrName)\n\n input.setAttribute(attrName, attribute ? `${attribute} ${describedbyIds}` : describedbyIds)\n }\n }, [describedbyIds])\n\n useEffect(() => {\n if (!autoBindErrorInput || !inputWrapperRef?.current) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attrName = 'aria-invalid'\n\n if (actualErrorMessages.length > 0) {\n input.setAttribute(attrName, 'true')\n } else {\n input.removeAttribute(attrName)\n }\n }\n }, [actualErrorMessages.length, autoBindErrorInput])\n\n let body = (\n <>\n <HelpMessageParagraph helpMessage={helpMessage} managedHtmlFor={managedHtmlFor} />\n <ExampleMessageText exampleMessage={exampleMessage} managedHtmlFor={managedHtmlFor} />\n <ErrorMessageList\n errorMessages={actualErrorMessages}\n managedHtmlFor={managedHtmlFor}\n classNames={classNames}\n />\n <div className={classNames.childrenWrapper} ref={inputWrapperRef}>\n {children}\n </div>\n <SupplementaryMessageText\n supplementaryMessage={supplementaryMessage}\n managedHtmlFor={managedHtmlFor}\n />\n </>\n )\n\n // HINT: dangerouslyTitleHiddenの場合、body以下の余白の計算を簡略化するため\n // Stackをネストし、そのStackに対してmargin-top: 0を指定する\n // こうすることでinner Stack以下の要素は擬似的にStackの最初の要素になる\n if (dangerouslyTitleHidden) {\n body = (\n <Stack gap={actualInnerMargin} className={classNames.underTitleStack}>\n {body}\n </Stack>\n )\n }\n\n return (\n <Stack\n {...props}\n as={as}\n gap={actualInnerMargin}\n aria-describedby={isFieldset && describedbyIds ? describedbyIds : undefined}\n className={classNames.wrapper}\n >\n <TitleCluster\n isFieldset={isFieldset}\n managedHtmlFor={managedHtmlFor}\n managedLabelId={managedLabelId}\n dangerouslyTitleHidden={dangerouslyTitleHidden}\n titleType={titleType}\n title={title}\n statusLabels={actualStatusLabels}\n subActionArea={subActionArea}\n labelClassName={classNames.label}\n />\n {body}\n </Stack>\n )\n}\n\nconst TitleCluster = memo<\n Pick<Props, 'dangerouslyTitleHidden' | 'title' | 'subActionArea'> & {\n titleType: TextProps['styleType']\n isFieldset: boolean\n managedHtmlFor: string\n managedLabelId: string\n labelClassName: string\n statusLabels: StatusLabelType[]\n }\n>(\n ({\n isFieldset,\n managedHtmlFor,\n managedLabelId,\n dangerouslyTitleHidden,\n titleType,\n title,\n subActionArea,\n labelClassName,\n statusLabels,\n }) => {\n const body = (\n <>\n <Text styleType={titleType}>{title}</Text>\n <StatusLabelCluster statusLabels={statusLabels} />\n </>\n )\n\n const attrs = useMemo(() => {\n if (dangerouslyTitleHidden) {\n return {\n label: null,\n visuallyHidden: isFieldset\n ? {\n as: 'legend',\n }\n : {\n as: 'label',\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n }\n }\n\n if (isFieldset) {\n return {\n label: { 'aria-hidden': 'true' } as const,\n visuallyHidden: { as: 'legend' },\n }\n }\n\n return {\n label: {\n as: 'label' as const,\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n visuallyHidden: null,\n }\n }, [managedLabelId, managedHtmlFor, dangerouslyTitleHidden, isFieldset])\n\n return (\n <>\n {attrs.visuallyHidden && (\n <VisuallyHiddenText {...attrs.visuallyHidden}>\n {\n // HINT: innerTextでは正しく文字が取得できない場合がある\n // 安全策としてinnerTextが空を取得してきたらbody自体を埋めこみます\n innerText(body) || body\n }\n </VisuallyHiddenText>\n )}\n {attrs.label && (\n <Cluster\n justify=\"space-between\"\n // HINT: UI上、常にトップの要素になるため、Stackの計算が狂わないよう、\n // 常にmargin-topを0にする\n className=\"[&&&]:shr--mt-0\"\n >\n <Cluster {...attrs.label} align=\"center\" className={labelClassName}>\n {body}\n </Cluster>\n {subActionArea && <div className=\"shr-grow\">{subActionArea}</div>}\n </Cluster>\n )}\n </>\n )\n },\n)\n\nconst StatusLabelCluster = memo<{ statusLabels: StatusLabelType[] }>(({ statusLabels }) =>\n statusLabels.length === 0 ? null : (\n <Cluster gap={0.25} as=\"span\">\n {statusLabels}\n </Cluster>\n ),\n)\n\nconst HelpMessageParagraph = memo<Pick<Props, 'helpMessage'> & { managedHtmlFor: string }>(\n ({ helpMessage, managedHtmlFor }) =>\n helpMessage ? (\n <p className=\"smarthr-ui-FormControl-helpMessage\" id={`${managedHtmlFor}_helpMessage`}>\n {helpMessage}\n </p>\n ) : null,\n)\n\nconst ExampleMessageText = memo<Pick<Props, 'exampleMessage'> & { managedHtmlFor: string }>(\n ({ exampleMessage, managedHtmlFor }) =>\n exampleMessage ? (\n <Text\n as=\"p\"\n color=\"TEXT_GREY\"\n italic\n id={`${managedHtmlFor}_exampleMessage`}\n className=\"smarthr-ui-FormControl-exampleMessage\"\n >\n {exampleMessage}\n </Text>\n ) : null,\n)\n\nconst ErrorMessageList = memo<{\n errorMessages: ReactNode[]\n managedHtmlFor: string\n classNames: {\n errorList: string\n errorIcon: string\n }\n}>(({ errorMessages, managedHtmlFor, classNames }) =>\n errorMessages.length > 0 ? (\n <div id={`${managedHtmlFor}_errorMessages`} className={classNames.errorList} role=\"alert\">\n {errorMessages.map((message, index) => (\n <p key={index}>\n <FaCircleExclamationIcon text={message} className={classNames.errorIcon} />\n </p>\n ))}\n </div>\n ) : null,\n)\n\nconst SupplementaryMessageText = memo<\n Pick<Props, 'supplementaryMessage'> & { managedHtmlFor: string }\n>(({ supplementaryMessage, managedHtmlFor }) =>\n supplementaryMessage ? (\n <Text\n as=\"p\"\n size=\"S\"\n color=\"TEXT_GREY\"\n id={`${managedHtmlFor}_supplementaryMessage`}\n className=\"smarthr-ui-FormControl-supplementaryMessage\"\n >\n {supplementaryMessage}\n </Text>\n ) : null,\n)\n\nexport const FormControl: FC<Omit<Props & ElementProps, 'as' | 'disabled'>> = ActualFormControl\n"]}
1
+ {"version":3,"file":"FormControl.js","sourceRoot":"","sources":["../../../src/components/FormControl/FormControl.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type ComponentType,\n type FC,\n Fragment,\n type FunctionComponentElement,\n type PropsWithChildren,\n type ReactNode,\n memo,\n useEffect,\n useMemo,\n useRef,\n} from 'react'\nimport { useId } from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { FaCircleExclamationIcon } from '../Icon'\nimport { Cluster, Stack } from '../Layout'\nimport { StatusLabel } from '../StatusLabel'\nimport { Text, type TextProps } from '../Text'\nimport { VisuallyHiddenText, visuallyHiddenTextClassNameGenerator } from '../VisuallyHiddenText'\n\nimport type { Gap } from '../../types'\n\ntype StatusLabelProps = ComponentProps<typeof StatusLabel>\ntype StatusLabelType = FunctionComponentElement<StatusLabelProps>\n\ntype Props = PropsWithChildren<{\n /** グループのタイトル名 */\n title: ReactNode\n /** タイトルの見出しのタイプ */\n titleType?: TextProps['styleType']\n /** タイトル右の領域 */\n subActionArea?: ReactNode\n /** タイトルの見出しを視覚的に隠すかどうか */\n dangerouslyTitleHidden?: boolean\n /** label 要素に適用する `htmlFor` 値 */\n htmlFor?: string\n /** label 要素に適用する `id` 値 */\n labelId?: string\n /** タイトル群と子要素の間の間隔調整用(基本的には不要) */\n innerMargin?: Gap\n /** タイトルの隣に表示する `StatusLabel` の Props の配列 */\n /**\n * @deprecated statusLabelProps属性は非推奨です。statusLabelsを使ってください。\n */\n statusLabelProps?: StatusLabelProps | StatusLabelProps[]\n /** タイトルの隣に表示する `StatusLabel` の配列 */\n statusLabels?: StatusLabelType | StatusLabelType[]\n /** タイトルの下に表示するヘルプメッセージ */\n helpMessage?: ReactNode\n /** タイトルの下に表示する入力例 */\n exampleMessage?: ReactNode\n /** タイトルの下に表示するエラーメッセージ */\n errorMessages?: ReactNode | ReactNode[]\n /** エラーがある場合に自動的に入力要素を error にするかどうか */\n autoBindErrorInput?: boolean\n /** フォームコントロールの下に表示する補足メッセージ */\n supplementaryMessage?: ReactNode\n /** `true` のとき、文字色を `TEXT_DISABLED` にする */\n disabled?: boolean\n as?: string | ComponentType<any>\n}>\ntype ElementProps = Omit<ComponentPropsWithoutRef<'div'>, keyof Props | 'aria-labelledby'>\n\nconst classNameGenerator = tv({\n slots: {\n wrapper: [\n 'smarthr-ui-FormControl',\n 'shr-mx-[unset] shr-border-none shr-p-[unset]',\n 'disabled:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-label_>_span]:shr-text-disabled',\n '[&:disabled_.smarthr-ui-FormControl-exampleMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-errorMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-FormControl-supplementaryMessage]:shr-text-color-inherit',\n '[&:disabled_.smarthr-ui-Input]:shr-border-default/50 [&:disabled_.smarthr-ui-Input]:shr-bg-white-darken',\n ],\n label: ['smarthr-ui-FormControl-label'],\n errorList: ['shr-list-none'],\n errorIcon: ['smarthr-ui-FormControl-errorMessage', 'shr-text-danger'],\n underTitleStack: ['[&&&]:shr-mt-0'],\n childrenWrapper: [],\n },\n variants: {\n innerMargin: {\n 0: {},\n 0.25: {},\n 0.5: {},\n 0.75: {},\n 1: {},\n 1.25: {},\n 1.5: {},\n 2: {},\n 2.5: {},\n 3: {},\n 3.5: {},\n 4: {},\n 8: {},\n X3S: {},\n XXS: {},\n XS: {},\n S: {},\n M: {},\n L: {},\n XL: {},\n XXL: {},\n X3L: {},\n } as { [key in Gap]: string },\n isFieldset: {\n true: {},\n false: {},\n },\n },\n compoundVariants: [\n // TODO: innerMarginが未指定、初期値の場合、かつFieldsetの場合、childrenの上部の余白を広げることで\n // FormControltとの差をわかりやすくしている\n // 微妙な方法ではあるので、必要に応じてinnerMarginではない属性を用意する\n // https://kufuinc.slack.com/archives/CGC58MW01/p1737944965871159?thread_ts=1737541173.404369&cid=CGC58MW01\n {\n innerMargin: undefined,\n isFieldset: true,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-1',\n },\n },\n {\n innerMargin: undefined,\n isFieldset: false,\n class: {\n childrenWrapper: '[:not([hidden])_~_&&&]:shr-mt-0.5',\n },\n },\n ],\n})\n\nconst SMARTHR_UI_INPUT_SELECTOR = '[data-smarthr-ui-input=\"true\"]'\n\nexport const ActualFormControl: FC<Props & ElementProps> = ({\n title,\n titleType = 'blockTitle',\n subActionArea,\n dangerouslyTitleHidden = false,\n htmlFor,\n labelId,\n innerMargin,\n statusLabels,\n statusLabelProps,\n helpMessage,\n exampleMessage,\n errorMessages,\n autoBindErrorInput = true,\n supplementaryMessage,\n as = 'div',\n className,\n children,\n ...props\n}) => {\n const defaultHtmlFor = useId()\n const defaultLabelId = useId()\n const managedHtmlFor = htmlFor || defaultHtmlFor\n const managedLabelId = labelId || defaultLabelId\n const inputWrapperRef = useRef<HTMLDivElement>(null)\n const isFieldset = as === 'fieldset'\n\n const describedbyIds = useMemo(() => {\n const temp = []\n\n if (helpMessage) {\n temp.push(`${managedHtmlFor}_helpMessage`)\n }\n if (exampleMessage) {\n temp.push(`${managedHtmlFor}_exampleMessage`)\n }\n if (supplementaryMessage) {\n temp.push(`${managedHtmlFor}_supplementaryMessage`)\n }\n if (errorMessages) {\n temp.push(`${managedHtmlFor}_errorMessages`)\n }\n\n return temp.join(' ')\n }, [helpMessage, exampleMessage, supplementaryMessage, errorMessages, managedHtmlFor])\n\n const actualStatusLabels = useMemo(() => {\n if (statusLabels) {\n return (Array.isArray(statusLabels) ? statusLabels : [statusLabels]).map(\n (statusLabel, index) => <Fragment key={index}>{statusLabel}</Fragment>,\n )\n }\n\n if (!statusLabelProps) {\n return []\n }\n\n const labelProps = Array.isArray(statusLabelProps) ? statusLabelProps : [statusLabelProps]\n\n return labelProps.map((prop, index) => <StatusLabel {...prop} key={index} />)\n }, [statusLabels, statusLabelProps])\n\n const actualErrorMessages = useMemo(() => {\n if (!errorMessages) {\n return []\n }\n\n return Array.isArray(errorMessages) ? errorMessages : [errorMessages]\n }, [errorMessages])\n\n const actualInnerMargin = useMemo(() => innerMargin ?? 0.5, [innerMargin])\n\n const classNames = useMemo(() => {\n const { wrapper, label, errorList, errorIcon, underTitleStack, childrenWrapper } =\n classNameGenerator({ innerMargin, isFieldset })\n\n return {\n wrapper: wrapper({ className }),\n label: label({\n className: dangerouslyTitleHidden ? visuallyHiddenTextClassNameGenerator() : '',\n }),\n errorList: errorList(),\n errorIcon: errorIcon(),\n underTitleStack: underTitleStack(),\n childrenWrapper: childrenWrapper(),\n }\n }, [innerMargin, isFieldset, dangerouslyTitleHidden, className])\n\n useEffect(() => {\n if (\n isFieldset ||\n !inputWrapperRef?.current ||\n // HINT: 対象idを持つ要素が既に存在する場合、何もしない\n document.getElementById(managedHtmlFor)\n ) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (!input) {\n return\n }\n\n if (!input.getAttribute('id')) {\n input.setAttribute('id', managedHtmlFor)\n }\n\n if (input instanceof HTMLInputElement && input.type === 'file') {\n const attrName = 'aria-labelledby'\n const inputLabelledByIds = input.getAttribute(attrName)\n\n if (inputLabelledByIds) {\n // InputFileの場合はlabel要素の可視ラベルをアクセシブルネームに含める\n input.setAttribute(attrName, `${inputLabelledByIds} ${managedLabelId}`)\n }\n }\n }, [managedHtmlFor, isFieldset, managedLabelId])\n\n useEffect(() => {\n if (!describedbyIds || !inputWrapperRef?.current) {\n return\n }\n\n const inputWrapper = inputWrapperRef.current\n const attrName = 'aria-describedby'\n\n if (inputWrapper.querySelector(`[${attrName}=\"${describedbyIds}\"]`)) {\n return\n }\n\n const input = inputWrapper.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attribute = input.getAttribute(attrName)\n\n input.setAttribute(attrName, attribute ? `${attribute} ${describedbyIds}` : describedbyIds)\n }\n }, [describedbyIds])\n\n useEffect(() => {\n if (!autoBindErrorInput || !inputWrapperRef?.current) {\n return\n }\n\n const input = inputWrapperRef.current.querySelector(SMARTHR_UI_INPUT_SELECTOR)\n\n if (input) {\n const attrName = 'aria-invalid'\n\n if (actualErrorMessages.length > 0) {\n input.setAttribute(attrName, 'true')\n } else {\n input.removeAttribute(attrName)\n }\n }\n }, [actualErrorMessages.length, autoBindErrorInput])\n\n // HINT: Fieldset内の可視ラベルが無いinputに、legend文言をアクセシブルネームに追加する\n // https://waic.jp/translations/WCAG21/Understanding/label-in-name.html\n useEffect(() => {\n if (!isFieldset || !inputWrapperRef.current) return\n const inputs = inputWrapperRef.current.querySelectorAll(SMARTHR_UI_INPUT_SELECTOR)\n if (!inputs.length) return\n\n const legendText = innerText(title)\n if (!legendText) return\n\n const labels = new Set(\n Array.from(document.querySelectorAll('label[for]')).map((label) => label.getAttribute('for')),\n )\n\n inputs.forEach((input) => {\n const inputId = input.getAttribute('id')\n if (inputId && labels.has(inputId)) {\n return\n }\n\n let accessibleName = ''\n if (input.hasAttribute('aria-label')) {\n accessibleName = input.getAttribute('aria-label') || ''\n } else if (input.hasAttribute('title')) {\n accessibleName = input.getAttribute('title') || ''\n }\n\n if (!accessibleName.includes(legendText)) {\n input.setAttribute('aria-label', `${legendText} ${accessibleName}`.trim())\n }\n })\n }, [isFieldset, title])\n\n let body = (\n <>\n <HelpMessageParagraph helpMessage={helpMessage} managedHtmlFor={managedHtmlFor} />\n <ExampleMessageText exampleMessage={exampleMessage} managedHtmlFor={managedHtmlFor} />\n <ErrorMessageList\n errorMessages={actualErrorMessages}\n managedHtmlFor={managedHtmlFor}\n classNames={classNames}\n />\n <div className={classNames.childrenWrapper} ref={inputWrapperRef}>\n {children}\n </div>\n <SupplementaryMessageText\n supplementaryMessage={supplementaryMessage}\n managedHtmlFor={managedHtmlFor}\n />\n </>\n )\n\n // HINT: dangerouslyTitleHiddenの場合、body以下の余白の計算を簡略化するため\n // Stackをネストし、そのStackに対してmargin-top: 0を指定する\n // こうすることでinner Stack以下の要素は擬似的にStackの最初の要素になる\n if (dangerouslyTitleHidden) {\n body = (\n <Stack gap={actualInnerMargin} className={classNames.underTitleStack}>\n {body}\n </Stack>\n )\n }\n\n return (\n <Stack\n {...props}\n as={as}\n gap={actualInnerMargin}\n aria-describedby={isFieldset && describedbyIds ? describedbyIds : undefined}\n className={classNames.wrapper}\n >\n <TitleCluster\n isFieldset={isFieldset}\n managedHtmlFor={managedHtmlFor}\n managedLabelId={managedLabelId}\n dangerouslyTitleHidden={dangerouslyTitleHidden}\n titleType={titleType}\n title={title}\n statusLabels={actualStatusLabels}\n subActionArea={subActionArea}\n labelClassName={classNames.label}\n />\n {body}\n </Stack>\n )\n}\n\nconst TitleCluster = memo<\n Pick<Props, 'dangerouslyTitleHidden' | 'title' | 'subActionArea'> & {\n titleType: TextProps['styleType']\n isFieldset: boolean\n managedHtmlFor: string\n managedLabelId: string\n labelClassName: string\n statusLabels: StatusLabelType[]\n }\n>(\n ({\n isFieldset,\n managedHtmlFor,\n managedLabelId,\n dangerouslyTitleHidden,\n titleType,\n title,\n subActionArea,\n labelClassName,\n statusLabels,\n }) => {\n const body = (\n <>\n <Text styleType={titleType}>{title}</Text>\n <StatusLabelCluster statusLabels={statusLabels} />\n </>\n )\n\n const attrs = useMemo(() => {\n if (dangerouslyTitleHidden) {\n return {\n label: null,\n visuallyHidden: isFieldset\n ? {\n as: 'legend',\n }\n : {\n as: 'label',\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n }\n }\n\n if (isFieldset) {\n return {\n label: { 'aria-hidden': 'true' } as const,\n visuallyHidden: { as: 'legend' },\n }\n }\n\n return {\n label: {\n as: 'label' as const,\n htmlFor: managedHtmlFor,\n id: managedLabelId,\n },\n visuallyHidden: null,\n }\n }, [managedLabelId, managedHtmlFor, dangerouslyTitleHidden, isFieldset])\n\n return (\n <>\n {attrs.visuallyHidden && (\n <VisuallyHiddenText {...attrs.visuallyHidden}>\n {\n // HINT: innerTextでは正しく文字が取得できない場合がある\n // 安全策としてinnerTextが空を取得してきたらbody自体を埋めこみます\n innerText(body) || body\n }\n </VisuallyHiddenText>\n )}\n {attrs.label && (\n <Cluster\n justify=\"space-between\"\n // HINT: UI上、常にトップの要素になるため、Stackの計算が狂わないよう、\n // 常にmargin-topを0にする\n className=\"[&&&]:shr--mt-0\"\n >\n <Cluster {...attrs.label} align=\"center\" className={labelClassName}>\n {body}\n </Cluster>\n {subActionArea && <div className=\"shr-grow\">{subActionArea}</div>}\n </Cluster>\n )}\n </>\n )\n },\n)\n\nconst StatusLabelCluster = memo<{ statusLabels: StatusLabelType[] }>(({ statusLabels }) =>\n statusLabels.length === 0 ? null : (\n <Cluster gap={0.25} as=\"span\">\n {statusLabels}\n </Cluster>\n ),\n)\n\nconst HelpMessageParagraph = memo<Pick<Props, 'helpMessage'> & { managedHtmlFor: string }>(\n ({ helpMessage, managedHtmlFor }) =>\n helpMessage ? (\n <p className=\"smarthr-ui-FormControl-helpMessage\" id={`${managedHtmlFor}_helpMessage`}>\n {helpMessage}\n </p>\n ) : null,\n)\n\nconst ExampleMessageText = memo<Pick<Props, 'exampleMessage'> & { managedHtmlFor: string }>(\n ({ exampleMessage, managedHtmlFor }) =>\n exampleMessage ? (\n <Text\n as=\"p\"\n color=\"TEXT_GREY\"\n italic\n id={`${managedHtmlFor}_exampleMessage`}\n className=\"smarthr-ui-FormControl-exampleMessage\"\n >\n {exampleMessage}\n </Text>\n ) : null,\n)\n\nconst ErrorMessageList = memo<{\n errorMessages: ReactNode[]\n managedHtmlFor: string\n classNames: {\n errorList: string\n errorIcon: string\n }\n}>(({ errorMessages, managedHtmlFor, classNames }) =>\n errorMessages.length > 0 ? (\n <div id={`${managedHtmlFor}_errorMessages`} className={classNames.errorList} role=\"alert\">\n {errorMessages.map((message, index) => (\n <p key={index}>\n <FaCircleExclamationIcon text={message} className={classNames.errorIcon} />\n </p>\n ))}\n </div>\n ) : null,\n)\n\nconst SupplementaryMessageText = memo<\n Pick<Props, 'supplementaryMessage'> & { managedHtmlFor: string }\n>(({ supplementaryMessage, managedHtmlFor }) =>\n supplementaryMessage ? (\n <Text\n as=\"p\"\n size=\"S\"\n color=\"TEXT_GREY\"\n id={`${managedHtmlFor}_supplementaryMessage`}\n className=\"smarthr-ui-FormControl-supplementaryMessage\"\n >\n {supplementaryMessage}\n </Text>\n ) : null,\n)\n\nexport const FormControl: FC<Omit<Props & ElementProps, 'as' | 'disabled'>> = ActualFormControl\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "smarthr-ui",
3
3
  "description": "SmartHR ui components built with React.",
4
- "version": "75.1.1",
4
+ "version": "75.2.0",
5
5
  "author": "SmartHR-UI Team",
6
6
  "dependencies": {
7
7
  "@smarthr/wareki": "^1.3.0",
@@ -29,14 +29,12 @@
29
29
  "@rollup/plugin-node-resolve": "^16.0.1",
30
30
  "@rollup/plugin-replace": "^6.0.2",
31
31
  "@rollup/plugin-typescript": "^12.1.4",
32
- "@storybook/addon-a11y": "9.0.16",
33
32
  "@storybook/addon-docs": "9.0.16",
34
33
  "@storybook/addon-styling-webpack": "^2.0.0",
35
34
  "@storybook/addon-webpack5-compiler-babel": "^3.0.6",
36
35
  "@storybook/cli": "9.0.16",
37
36
  "@storybook/react": "9.0.16",
38
37
  "@storybook/react-webpack5": "9.0.16",
39
- "@storybook/test-runner": "^0.23.0",
40
38
  "@swc/core": "^1.12.11",
41
39
  "@testing-library/jest-dom": "^6.6.3",
42
40
  "@testing-library/react": "^16.3.0",
@@ -49,7 +47,6 @@
49
47
  "@types/react-transition-group": "^4.4.12",
50
48
  "@types/styled-components": "^5.1.34",
51
49
  "autoprefixer": "^10.4.21",
52
- "axe-playwright": "^2.1.0",
53
50
  "babel-loader": "^10.0.0",
54
51
  "babel-plugin-polyfill-corejs2": "^0.4.14",
55
52
  "babel-plugin-polyfill-regenerator": "^0.6.5",
@@ -187,7 +184,6 @@
187
184
  "dev": "run-s storybook",
188
185
  "test": "vitest",
189
186
  "test:build-assets": "node scripts/build-test.ts",
190
- "test-storybook:ci": "wait-on tcp:6006 && pnpm test-storybook --maxWorkers=2 --junit --skipTags='skip-test-runner'",
191
187
  "chromatic": "chromatic",
192
188
  "write:ui-props": "ts-node scripts/exportUIProps.ts",
193
189
  "export:ui-props": "run-s build:lib write:ui-props",