@lifesg/react-design-system 4.0.0-alpha.12 → 4.0.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/breadcrumb/breadcrumb.js +1 -1
- package/breadcrumb/breadcrumb.js.map +1 -1
- package/breadcrumb/breadcrumb.styles.d.ts +0 -8
- package/breadcrumb/breadcrumb.styles.js +1 -1
- package/breadcrumb/breadcrumb.styles.js.map +1 -1
- package/breadcrumb/breadcrumb.styles_1blzn8n.css +7 -0
- package/breadcrumb/types.d.ts +2 -13
- package/cjs/breadcrumb/breadcrumb.js +1 -1
- package/cjs/breadcrumb/breadcrumb.js.map +1 -1
- package/cjs/breadcrumb/breadcrumb.styles.js +1 -1
- package/cjs/breadcrumb/breadcrumb.styles.js.map +1 -1
- package/cjs/breadcrumb/breadcrumb.styles_1blzn8n.css +7 -0
- package/cjs/drawer/drawer.js +1 -1
- package/cjs/drawer/drawer.js.map +1 -1
- package/cjs/drawer/drawer.styles.js +1 -1
- package/cjs/drawer/drawer.styles.js.map +1 -1
- package/{drawer/drawer.styles_94hx3y.css → cjs/drawer/drawer.styles_a4bwe0.css} +6 -3
- package/cjs/file-upload/file-list/file-list.js +1 -1
- package/cjs/file-upload/file-list/file-list.js.map +1 -1
- package/cjs/file-upload/file-list/file-list.styles.js +1 -1
- package/cjs/file-upload/file-list/file-list.styles.js.map +1 -1
- package/cjs/file-upload/file-list/file-list.styles_1xbypnh.css +2 -0
- package/cjs/file-upload/file-list-item/file-item-actions.js +2 -0
- package/cjs/file-upload/file-list-item/file-item-actions.js.map +1 -0
- package/cjs/file-upload/file-list-item/file-item-actions.styles.js +2 -0
- package/cjs/file-upload/file-list-item/file-item-actions.styles.js.map +1 -0
- package/cjs/file-upload/file-list-item/file-item-actions.styles_1quulgy.css +8 -0
- package/cjs/file-upload/file-list-item/file-item-details.js +2 -0
- package/cjs/file-upload/file-list-item/file-item-details.js.map +1 -0
- package/cjs/file-upload/file-list-item/file-item-details.styles.js +2 -0
- package/cjs/file-upload/file-list-item/file-item-details.styles.js.map +1 -0
- package/cjs/file-upload/file-list-item/file-item-details.styles_1ce9akx.css +7 -0
- package/cjs/file-upload/file-list-item/file-list-item.js +1 -1
- package/cjs/file-upload/file-list-item/file-list-item.js.map +1 -1
- package/cjs/file-upload/file-list-item/file-list-item.styles.js +1 -1
- package/cjs/file-upload/file-list-item/file-list-item.styles.js.map +1 -1
- package/cjs/file-upload/file-list-item/file-list-item.styles_x3a500.css +10 -0
- package/cjs/file-upload/file-list-item/use-truncated-name.js +2 -0
- package/cjs/file-upload/file-list-item/use-truncated-name.js.map +1 -0
- package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.js +1 -1
- package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.js.map +1 -1
- package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js +1 -1
- package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js.map +1 -1
- package/cjs/local-nav/local-nav-dropdown/{local-nav-dropdown.styles_uhd4xl.css → local-nav-dropdown.styles_kpel1m.css} +9 -6
- package/cjs/local-nav/local-nav-menu/local-nav-menu.js +1 -1
- package/cjs/local-nav/local-nav-menu/local-nav-menu.js.map +1 -1
- package/cjs/local-nav/local-nav-menu/local-nav-menu.styles.js +1 -1
- package/cjs/local-nav/local-nav-menu/local-nav-menu.styles.js.map +1 -1
- package/cjs/local-nav/local-nav-menu/local-nav-menu.styles_67fcyc.css +8 -0
- package/cjs/menu/menu-content.js +1 -1
- package/cjs/menu/menu-content.js.map +1 -1
- package/cjs/menu/menu-content.styles.js +1 -1
- package/cjs/menu/menu-content.styles.js.map +1 -1
- package/cjs/menu/menu-content.styles_1r8oxtk.css +5 -0
- package/cjs/menu/menu-item.styles.js +1 -1
- package/cjs/menu/menu-item.styles.js.map +1 -1
- package/cjs/menu/{menu-item.styles_liyed8.css → menu-item.styles_eut2j9.css} +1 -1
- package/cjs/menu/menu-link.styles.js +1 -1
- package/cjs/menu/menu-link.styles.js.map +1 -1
- package/cjs/menu/menu-link.styles_rng7n7.css +2 -0
- package/cjs/menu/menu-section.js +1 -1
- package/cjs/menu/menu-section.js.map +1 -1
- package/cjs/menu/menu-section.styles.js +1 -1
- package/cjs/menu/menu-section.styles.js.map +1 -1
- package/cjs/menu/menu-section.styles_1n0ppxl.css +5 -0
- package/cjs/navbar/navbar-items.js +1 -1
- package/cjs/navbar/navbar-items.js.map +1 -1
- package/cjs/notification-banner/notification-banner-hoc.js +1 -1
- package/cjs/notification-banner/notification-banner-hoc.js.map +1 -1
- package/cjs/pagination/pagination.js +1 -1
- package/cjs/pagination/pagination.js.map +1 -1
- package/cjs/shared/dropdown-list/dropdown-label.js +1 -1
- package/cjs/shared/dropdown-list/dropdown-label.js.map +1 -1
- package/cjs/shared/fade-wrapper/fade-wrapper.js +1 -1
- package/cjs/shared/fade-wrapper/fade-wrapper.js.map +1 -1
- package/cjs/theme/tokens/colour.js +1 -1
- package/cjs/theme/tokens/colour.js.map +1 -1
- package/cjs/theme/tokens/radius.js +1 -1
- package/cjs/theme/tokens/radius.js.map +1 -1
- package/codemods/run-codemod.js +8 -2
- package/codemods/run-codemod.js.map +1 -1
- package/codemods/run-codemod.ts +13 -4
- package/drawer/drawer.d.ts +1 -1
- package/drawer/drawer.js +1 -1
- package/drawer/drawer.js.map +1 -1
- package/drawer/drawer.styles.d.ts +3 -0
- package/drawer/drawer.styles.js +1 -1
- package/drawer/drawer.styles.js.map +1 -1
- package/{cjs/drawer/drawer.styles_94hx3y.css → drawer/drawer.styles_a4bwe0.css} +6 -3
- package/drawer/types.d.ts +7 -0
- package/file-upload/file-list/file-list.js +1 -1
- package/file-upload/file-list/file-list.js.map +1 -1
- package/file-upload/file-list/file-list.styles.d.ts +0 -1
- package/file-upload/file-list/file-list.styles.js +1 -1
- package/file-upload/file-list/file-list.styles.js.map +1 -1
- package/file-upload/file-list/file-list.styles_1xbypnh.css +2 -0
- package/file-upload/file-list-item/file-item-actions.d.ts +30 -0
- package/file-upload/file-list-item/file-item-actions.js +2 -0
- package/file-upload/file-list-item/file-item-actions.js.map +1 -0
- package/file-upload/file-list-item/file-item-actions.styles.d.ts +6 -0
- package/file-upload/file-list-item/file-item-actions.styles.js +2 -0
- package/file-upload/file-list-item/file-item-actions.styles.js.map +1 -0
- package/file-upload/file-list-item/file-item-actions.styles_1quulgy.css +8 -0
- package/file-upload/file-list-item/file-item-details.d.ts +15 -0
- package/file-upload/file-list-item/file-item-details.js +2 -0
- package/file-upload/file-list-item/file-item-details.js.map +1 -0
- package/file-upload/file-list-item/file-item-details.styles.d.ts +7 -0
- package/file-upload/file-list-item/file-item-details.styles.js +2 -0
- package/file-upload/file-list-item/file-item-details.styles.js.map +1 -0
- package/file-upload/file-list-item/file-item-details.styles_1ce9akx.css +7 -0
- package/file-upload/file-list-item/file-list-item.d.ts +1 -7
- package/file-upload/file-list-item/file-list-item.js +1 -1
- package/file-upload/file-list-item/file-list-item.js.map +1 -1
- package/file-upload/file-list-item/file-list-item.styles.d.ts +6 -13
- package/file-upload/file-list-item/file-list-item.styles.js +1 -1
- package/file-upload/file-list-item/file-list-item.styles.js.map +1 -1
- package/file-upload/file-list-item/file-list-item.styles_x3a500.css +10 -0
- package/file-upload/file-list-item/types.d.ts +10 -3
- package/file-upload/file-list-item/use-truncated-name.d.ts +1 -0
- package/file-upload/file-list-item/use-truncated-name.js +2 -0
- package/file-upload/file-list-item/use-truncated-name.js.map +1 -0
- package/input-group/types.d.ts +4 -1
- package/input-select/types.d.ts +1 -0
- package/local-nav/local-nav-dropdown/local-nav-dropdown.js +1 -1
- package/local-nav/local-nav-dropdown/local-nav-dropdown.js.map +1 -1
- package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.d.ts +3 -0
- package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js +1 -1
- package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js.map +1 -1
- package/local-nav/local-nav-dropdown/{local-nav-dropdown.styles_uhd4xl.css → local-nav-dropdown.styles_kpel1m.css} +9 -6
- package/local-nav/local-nav-menu/local-nav-menu.js +1 -1
- package/local-nav/local-nav-menu/local-nav-menu.js.map +1 -1
- package/local-nav/local-nav-menu/local-nav-menu.styles.d.ts +3 -0
- package/local-nav/local-nav-menu/local-nav-menu.styles.js +1 -1
- package/local-nav/local-nav-menu/local-nav-menu.styles.js.map +1 -1
- package/local-nav/local-nav-menu/local-nav-menu.styles_67fcyc.css +8 -0
- package/local-nav/types.d.ts +17 -0
- package/masked-input/types.d.ts +1 -2
- package/menu/menu-content.js +1 -1
- package/menu/menu-content.js.map +1 -1
- package/menu/menu-content.styles.d.ts +1 -0
- package/menu/menu-content.styles.js +1 -1
- package/menu/menu-content.styles.js.map +1 -1
- package/menu/menu-content.styles_1r8oxtk.css +5 -0
- package/menu/menu-item.styles.js +1 -1
- package/menu/menu-item.styles.js.map +1 -1
- package/menu/{menu-item.styles_liyed8.css → menu-item.styles_eut2j9.css} +1 -1
- package/menu/menu-link.styles.js +1 -1
- package/menu/menu-link.styles.js.map +1 -1
- package/menu/menu-link.styles_rng7n7.css +2 -0
- package/menu/menu-section.d.ts +1 -1
- package/menu/menu-section.js +1 -1
- package/menu/menu-section.js.map +1 -1
- package/menu/menu-section.styles.d.ts +7 -0
- package/menu/menu-section.styles.js +1 -1
- package/menu/menu-section.styles.js.map +1 -1
- package/menu/menu-section.styles_1n0ppxl.css +5 -0
- package/menu/menu.d.ts +1 -1
- package/menu/types.d.ts +10 -2
- package/navbar/navbar-items.js +1 -1
- package/navbar/navbar-items.js.map +1 -1
- package/navbar/types.d.ts +9 -0
- package/notification-banner/notification-banner-hoc.js +1 -1
- package/notification-banner/notification-banner-hoc.js.map +1 -1
- package/package.json +2 -3
- package/pagination/pagination.js +1 -1
- package/pagination/pagination.js.map +1 -1
- package/pagination/types.d.ts +8 -1
- package/select-histogram/types.d.ts +1 -0
- package/shared/dropdown-list/dropdown-label.d.ts +2 -2
- package/shared/dropdown-list/dropdown-label.js +1 -1
- package/shared/dropdown-list/dropdown-label.js.map +1 -1
- package/shared/dropdown-list/types.d.ts +6 -2
- package/shared/fade-wrapper/fade-wrapper.js +1 -1
- package/shared/fade-wrapper/fade-wrapper.js.map +1 -1
- package/shared/fade-wrapper/types.d.ts +1 -0
- package/shared/styles/index.d.ts +5 -0
- package/theme/styles/a11y-playground.css +52 -40
- package/theme/styles/bookingsg.css +52 -40
- package/theme/styles/careercompass.css +52 -40
- package/theme/styles/ccube.css +52 -40
- package/theme/styles/imda.css +52 -40
- package/theme/styles/lifesg.css +52 -40
- package/theme/styles/mylegacy.css +52 -40
- package/theme/styles/oneservice.css +52 -40
- package/theme/styles/pa.css +28 -22
- package/theme/styles/rbs.css +52 -40
- package/theme/styles/sgw-digital-lobby.css +52 -40
- package/theme/styles/smgs.css +52 -40
- package/theme/styles/spf.css +28 -27
- package/theme/styles/sportsg-orange.css +52 -40
- package/theme/styles/supportgowhere.css +52 -40
- package/theme/styles/tote-board.css +52 -40
- package/theme/styles/vica.css +52 -40
- package/theme/styles/websg.css +52 -40
- package/theme/styles/wise.css +52 -40
- package/theme/styles/wogaa.css +52 -40
- package/theme/tokens/colour.d.ts +6 -0
- package/theme/tokens/colour.js +1 -1
- package/theme/tokens/colour.js.map +1 -1
- package/theme/tokens/radius.d.ts +3 -0
- package/theme/tokens/radius.js +1 -1
- package/theme/tokens/radius.js.map +1 -1
- package/breadcrumb/breadcrumb.styles_1nl4aua.css +0 -18
- package/cjs/breadcrumb/breadcrumb.styles_1nl4aua.css +0 -18
- package/cjs/file-upload/file-item-edit.js +0 -2
- package/cjs/file-upload/file-item-edit.js.map +0 -1
- package/cjs/file-upload/file-item-edit.styles.js +0 -2
- package/cjs/file-upload/file-item-edit.styles.js.map +0 -1
- package/cjs/file-upload/file-item-edit.styles_19bvztn.css +0 -11
- package/cjs/file-upload/file-list/file-list.styles_1w7so98.css +0 -3
- package/cjs/file-upload/file-list-item/file-list-item.styles_kgtfke.css +0 -17
- package/cjs/local-nav/local-nav-menu/local-nav-menu.styles_pq19dh.css +0 -5
- package/cjs/menu/menu-content.styles_p7ijll.css +0 -2
- package/cjs/menu/menu-link.styles_hfxoez.css +0 -2
- package/cjs/menu/menu-section.styles_g7my4d.css +0 -3
- package/file-upload/file-item-edit.d.ts +0 -14
- package/file-upload/file-item-edit.js +0 -2
- package/file-upload/file-item-edit.js.map +0 -1
- package/file-upload/file-item-edit.styles.d.ts +0 -9
- package/file-upload/file-item-edit.styles.js +0 -2
- package/file-upload/file-item-edit.styles.js.map +0 -1
- package/file-upload/file-item-edit.styles_19bvztn.css +0 -11
- package/file-upload/file-list/file-list.styles_1w7so98.css +0 -3
- package/file-upload/file-list-item/file-list-item.styles_kgtfke.css +0 -17
- package/local-nav/local-nav-menu/local-nav-menu.styles_pq19dh.css +0 -5
- package/menu/menu-content.styles_p7ijll.css +0 -2
- package/menu/menu-link.styles_hfxoez.css +0 -2
- package/menu/menu-section.styles_g7my4d.css +0 -3
package/codemods/run-codemod.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { checkbox, confirm, input, select } from "@inquirer/prompts";
|
|
2
|
-
import {
|
|
2
|
+
import { execFileSync } from "child_process";
|
|
3
3
|
import * as fs from "fs";
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
|
|
@@ -54,14 +54,23 @@ function runCodemods(selection: UserSelection): void {
|
|
|
54
54
|
|
|
55
55
|
selectedCodemods.forEach((codemod) => {
|
|
56
56
|
const codemodPath = path.join(codemodsDir, codemod, "index.ts");
|
|
57
|
-
let command = `npx --yes jscodeshift --parser=tsx -t ${codemodPath} ${targetPath}`;
|
|
58
|
-
|
|
59
57
|
console.log(
|
|
60
58
|
`Running codemod: ${codemod} on target path: ${targetPath}`
|
|
61
59
|
);
|
|
62
60
|
|
|
63
61
|
try {
|
|
64
|
-
|
|
62
|
+
execFileSync(
|
|
63
|
+
"npx",
|
|
64
|
+
[
|
|
65
|
+
"--yes",
|
|
66
|
+
"jscodeshift",
|
|
67
|
+
"--parser=tsx",
|
|
68
|
+
"-t",
|
|
69
|
+
codemodPath,
|
|
70
|
+
targetPath,
|
|
71
|
+
],
|
|
72
|
+
{ stdio: "inherit" }
|
|
73
|
+
);
|
|
65
74
|
console.log(
|
|
66
75
|
`Codemod ${codemod} executed successfully on ${targetPath}`
|
|
67
76
|
);
|
package/drawer/drawer.d.ts
CHANGED
|
@@ -7,4 +7,4 @@ import type { DrawerProps } from "./types";
|
|
|
7
7
|
* while it is open and returns to the triggering element on close.
|
|
8
8
|
* @keywords overlay, panel, side panel, tray
|
|
9
9
|
*/
|
|
10
|
-
export declare const Drawer: ({ children, heading, show, onClose, onOverlayClick, className, ...otherProps }: DrawerProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare const Drawer: ({ children, heading, show, onClose, onOverlayClick, customCallToAction, className, ...otherProps }: DrawerProps) => import("react/jsx-runtime").JSX.Element;
|
package/drawer/drawer.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{__rest as e}from"../_virtual/_tslib.js";import{jsx as r,jsxs as
|
|
1
|
+
import{__rest as e}from"../_virtual/_tslib.js";import{jsx as r,jsxs as t}from"react/jsx-runtime";import{useFloating as o,useDismiss as i,useInteractions as s,useTransitionStatus as a,FloatingFocusManager as l}from"@floating-ui/react";import{CrossIcon as n}from"@lifesg/react-icons/cross";import m from"clsx";import{useState as c,useRef as d,useEffect as p}from"react";import{useResizeDetector as h}from"../external/react-resize-detector/build/index.esm.js";import{Overlay as u}from"../overlay/overlay.js";import{ClickableIcon as f}from"../shared/clickable-icon/clickable-icon.js";import"../theme/theme-provider/index.js";import{parsePxOrRemValue as j}from"../theme/utils/css-variable.js";import"../theme/theme-provider/context.js";import"../theme/tokens/border.js";import{Breakpoint as v}from"../theme/tokens/breakpoint.js";import"../theme/tokens/colour.js";import"../theme/tokens/font.js";import"../theme/tokens/media-query.js";import"../util/calendar-helper.js";import"../external/dayjs/dayjs.min.js";import{useId as g}from"../util/simple-id-generator.js";import"../util/string-helper.js";import"@react-aria/live-announcer";import{useResolvedBreakpointToken as y}from"../theme/utils/use-media-query.js";import{Typography as b}from"../typography/index.js";import{container as k,heading as x,callToAction as C,callToActionStacked as N,header as w,headerStacked as O,content as T,closeButton as F}from"./drawer.styles.js";const H=H=>{var{children:M,heading:q,show:z,onClose:A,onOverlayClick:P,customCallToAction:R,className:_}=H,D=e(H,["children","heading","show","onClose","onOverlayClick","customCallToAction","className"]);const[E,I]=c(z),[K,Y]=c(),B=g(),G=d(null),{width:J,ref:L}=h({refreshMode:"throttle",refreshRate:300,onResize:()=>{const e=G.current;e&&Y(e.offsetTop+e.offsetHeight/2)}}),Q=j(y(v["sm-max"])),S=void 0!==J&&J<=Q,{context:U,refs:V}=o({open:z,onOpenChange:e=>{!e&&A&&A()}}),W=i(U,{escapeKey:!0,outsidePress:!1}),{getFloatingProps:X}=s([W]),{isMounted:Z,status:$}=a(U,{duration:800});p((()=>{if(!z){const e=setTimeout((()=>I(!1)),800);return()=>clearTimeout(e)}I(!0)}),[z]);return r(u,{show:E,enableOverlayClick:!0,onOverlayClick:P,children:Z?r(l,{context:U,initialFocus:-1,returnFocus:!0,children:t("div",Object.assign({ref:V.setFloating,"data-status":$,"data-testid":"drawer","aria-modal":!0,role:"dialog","aria-labelledby":B,onTransitionEnd:e=>{var r;"visibility"===e.propertyName&&z&&(null===(r=G.current)||void 0===r||r.focus())},className:m(k,_)},X(),D,{children:[t("div",{ref:L,className:m(w,S&&O),children:[r(b.HeadingMD,{as:"h2",className:x,id:B,ref:G,tabIndex:-1,weight:"bold",children:q}),R?r("div",{className:m(C,S&&N),children:R}):null]}),r("div",{className:T,children:M}),r(f,{"aria-label":"Close drawer",onClick:A,focusHighlight:!1,className:F,style:void 0!==K?{top:K,transform:"translateY(-50%)"}:void 0,children:r(n,{"aria-hidden":!0})})]}))}):void 0})};export{H as Drawer};
|
|
2
2
|
//# sourceMappingURL=drawer.js.map
|
package/drawer/drawer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.js","sources":["../../src/drawer/drawer.tsx"],"sourcesContent":["import {\n FloatingFocusManager,\n useDismiss,\n useFloating,\n useInteractions,\n useTransitionStatus,\n} from \"@floating-ui/react\";\nimport { CrossIcon } from \"@lifesg/react-icons/cross\";\nimport clsx from \"clsx\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { Overlay } from \"../overlay\";\nimport { ClickableIcon } from \"../shared/clickable-icon\";\nimport { Typography } from \"../typography\";\nimport { useId } from \"../util\";\nimport * as styles from \"./drawer.styles\";\nimport type { DrawerProps } from \"./types\";\n\n// @catalog\n/**\n * A slide-in panel that overlays the page content.\n *\n * Use `Drawer` to surface supplementary content or navigation options without\n * navigating away from the current page. Focus is trapped inside the dialog\n * while it is open and returns to the triggering element on close.\n * @keywords overlay, panel, side panel, tray\n */\nexport const Drawer = ({\n children,\n heading,\n show,\n onClose,\n onOverlayClick,\n className,\n ...otherProps\n}: DrawerProps) => {\n // =========================================================================\n // CONST, STATE, REFS\n // =========================================================================\n const [showOverlay, setShowOverlay] = useState(show);\n const id = useId();\n const initialFocusRef = useRef<HTMLHeadingElement>(null);\n\n // =========================================================================\n // FLOATING UI CONFIG\n // =========================================================================\n const { context, refs } = useFloating({\n open: show,\n onOpenChange: (open) => {\n if (!open && onClose) {\n onClose();\n }\n },\n });\n\n const dismiss = useDismiss(context, {\n escapeKey: true,\n outsidePress: false, // defer to Overlay click\n });\n\n const { getFloatingProps } = useInteractions([dismiss]);\n\n const { isMounted, status } = useTransitionStatus(context, {\n duration: 800,\n });\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEffect(() => {\n if (!show) {\n const timer = setTimeout(() => setShowOverlay(false), 800);\n return () => clearTimeout(timer);\n } else {\n setShowOverlay(true);\n }\n }, [show]);\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleDialogVisibility = (e: React.TransitionEvent) => {\n if (e.propertyName === \"visibility\" && show) {\n // focus the first element so that the screenreader enters the dialog\n initialFocusRef.current?.focus();\n }\n };\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n return (\n <Overlay\n show={showOverlay}\n enableOverlayClick\n onOverlayClick={onOverlayClick}\n >\n {isMounted ? (\n <FloatingFocusManager\n context={context}\n initialFocus={-1}\n returnFocus={true}\n >\n <div\n ref={refs.setFloating}\n data-status={status}\n data-testid=\"drawer\"\n aria-modal\n role=\"dialog\"\n aria-labelledby={id}\n onTransitionEnd={handleDialogVisibility}\n className={clsx(styles.container, className)}\n {...getFloatingProps()}\n {...otherProps}\n >\n <div className={styles.header}>\n <Typography.HeadingMD\n as=\"h2\"\n className={styles.heading}\n id={id}\n ref={initialFocusRef}\n tabIndex={-1}\n weight=\"bold\"\n >\n {heading}\n </Typography.HeadingMD>\n </div>\n <div className={styles.content}>{children}</div>\n <ClickableIcon\n aria-label=\"Close drawer\"\n onClick={onClose}\n focusHighlight={false}\n className={styles.closeButton}\n >\n <CrossIcon aria-hidden />\n </ClickableIcon>\n </div>\n </FloatingFocusManager>\n ) : undefined}\n </Overlay>\n );\n};\n"],"names":["Drawer","_a","children","heading","show","onClose","onOverlayClick","className","otherProps","__rest","showOverlay","setShowOverlay","useState","id","useId","initialFocusRef","useRef","context","refs","useFloating","open","onOpenChange","dismiss","useDismiss","escapeKey","outsidePress","getFloatingProps","useInteractions","isMounted","status","useTransitionStatus","duration","useEffect","timer","setTimeout","clearTimeout","_jsx","Overlay","enableOverlayClick","FloatingFocusManager","initialFocus","returnFocus","_jsxs","ref","setFloating","role","onTransitionEnd","e","propertyName","current","focus","clsx","styles.container","styles.header","Typography","HeadingMD","as","styles.heading","tabIndex","weight","styles.content","ClickableIcon","onClick","focusHighlight","styles.closeButton","CrossIcon","undefined"],"mappings":"00BA2BO,MAAMA,EAAUC,IAAA,IAAAC,SACnBA,EAAQC,QACRA,EAAOC,KACPA,EAAIC,QACJA,EAAOC,eACPA,EAAcC,UACdA,GAASN,EACNO,EAAUC,EAAAR,EAPM,sEAYnB,MAAOS,EAAaC,GAAkBC,EAASR,GACzCS,EAAKC,IACLC,EAAkBC,EAA2B,OAK7CC,QAAEA,EAAOC,KAAEA,GAASC,EAAY,CAClCC,KAAMhB,EACNiB,aAAeD,KACNA,GAAQf,GACTA,GACJ,IAIFiB,EAAUC,EAAWN,EAAS,CAChCO,WAAW,EACXC,cAAc,KAGZC,iBAAEA,GAAqBC,EAAgB,CAACL,KAExCM,UAAEA,EAASC,OAAEA,GAAWC,EAAoBb,EAAS,CACvDc,SAAU,MAMdC,GAAU,KACN,IAAK5B,EAAM,CACP,MAAM6B,EAAQC,YAAW,IAAMvB,GAAe,IAAQ,KACtD,MAAO,IAAMwB,aAAaF,EAC9B,CACItB,GAAe,EACnB,GACD,CAACP,IAeJ,OACIgC,EAACC,EAAO,CACJjC,KAAMM,EACN4B,oBAAkB,EAClBhC,eAAgBA,EAAcJ,SAE7B0B,EACGQ,EAACG,EAAoB,CACjBtB,QAASA,EACTuB,cAAc,EACdC,aAAa,EAAIvC,SAEjBwC,uBACIC,IAAKzB,EAAK0B,YAAW,cACRf,EAAM,cACP,yBAEZgB,KAAK,SAAQ,kBACIhC,EACjBiC,gBA7BYC,UACL,eAAnBA,EAAEC,cAAiC5C,IAEZ,QAAvBH,EAAAc,EAAgBkC,eAAO,IAAAhD,GAAAA,EAAEiD,QAC7B,EA0BgB3C,UAAW4C,EAAKC,EAAkB7C,IAC9BmB,IACAlB,EAAU,CAAAN,SAAA,CAEdkC,EAAA,MAAA,CAAK7B,UAAW8C,EAAanD,SACzBkC,EAACkB,EAAWC,UAAS,CACjBC,GAAG,KACHjD,UAAWkD,EACX5C,GAAIA,EACJ8B,IAAK5B,EACL2C,UAAU,EACVC,OAAO,gBAENxD,MAGTiC,SAAK7B,UAAWqD,EAAc1D,SAAGA,IACjCkC,EAACyB,EAAa,CAAA,aACC,eACXC,QAASzD,EACT0D,gBAAgB,EAChBxD,UAAWyD,EAAkB9D,SAE7BkC,EAAC6B,EAAS,CAAA,eAAA,iBAItBC,GACE"}
|
|
1
|
+
{"version":3,"file":"drawer.js","sources":["../../src/drawer/drawer.tsx"],"sourcesContent":["import {\n FloatingFocusManager,\n useDismiss,\n useFloating,\n useInteractions,\n useTransitionStatus,\n} from \"@floating-ui/react\";\nimport { CrossIcon } from \"@lifesg/react-icons/cross\";\nimport clsx from \"clsx\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useResizeDetector } from \"react-resize-detector\";\n\nimport { Overlay } from \"../overlay\";\nimport { ClickableIcon } from \"../shared/clickable-icon\";\nimport {\n Breakpoint,\n parsePxOrRemValue,\n useResolvedBreakpointToken,\n} from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { useId } from \"../util\";\nimport * as styles from \"./drawer.styles\";\nimport type { DrawerProps } from \"./types\";\n\n// @catalog\n/**\n * A slide-in panel that overlays the page content.\n *\n * Use `Drawer` to surface supplementary content or navigation options without\n * navigating away from the current page. Focus is trapped inside the dialog\n * while it is open and returns to the triggering element on close.\n * @keywords overlay, panel, side panel, tray\n */\nexport const Drawer = ({\n children,\n heading,\n show,\n onClose,\n onOverlayClick,\n customCallToAction,\n className,\n ...otherProps\n}: DrawerProps) => {\n // =========================================================================\n // CONST, STATE, REFS\n // =========================================================================\n const [showOverlay, setShowOverlay] = useState(show);\n // vertical centre of the heading, used to align the (last-in-DOM,\n // absolutely positioned) close button with it\n const [closeButtonTop, setCloseButtonTop] = useState<number>();\n const id = useId();\n const initialFocusRef = useRef<HTMLHeadingElement>(null);\n // Observe the header (whose size tracks the drawer's actual width, which\n // consumers may override) to drive layout in one pass: the width decides\n // call-to-action stacking, and each resize re-centres the close button on\n // the heading. offsetTop/offsetHeight are relative to the drawer (the\n // positioned ancestor), so they share the close button's coordinate space.\n const { width: headerWidth, ref: headerRef } =\n useResizeDetector<HTMLDivElement>({\n refreshMode: \"throttle\",\n refreshRate: 300,\n onResize: () => {\n const headingEl = initialFocusRef.current;\n if (headingEl) {\n setCloseButtonTop(\n headingEl.offsetTop + headingEl.offsetHeight / 2\n );\n }\n },\n });\n // stack the call-to-action below the heading once the drawer is too narrow\n // to fit both on one line\n const stackWidth = parsePxOrRemValue(\n useResolvedBreakpointToken(Breakpoint[\"sm-max\"])\n );\n const stackCallToAction =\n headerWidth !== undefined && headerWidth <= stackWidth;\n\n // =========================================================================\n // FLOATING UI CONFIG\n // =========================================================================\n const { context, refs } = useFloating({\n open: show,\n onOpenChange: (open) => {\n if (!open && onClose) {\n onClose();\n }\n },\n });\n\n const dismiss = useDismiss(context, {\n escapeKey: true,\n outsidePress: false, // defer to Overlay click\n });\n\n const { getFloatingProps } = useInteractions([dismiss]);\n\n const { isMounted, status } = useTransitionStatus(context, {\n duration: 800,\n });\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEffect(() => {\n if (!show) {\n const timer = setTimeout(() => setShowOverlay(false), 800);\n return () => clearTimeout(timer);\n } else {\n setShowOverlay(true);\n }\n }, [show]);\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleDialogVisibility = (e: React.TransitionEvent) => {\n if (e.propertyName === \"visibility\" && show) {\n // focus the first element so that the screenreader enters the dialog\n initialFocusRef.current?.focus();\n }\n };\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n return (\n <Overlay\n show={showOverlay}\n enableOverlayClick\n onOverlayClick={onOverlayClick}\n >\n {isMounted ? (\n <FloatingFocusManager\n context={context}\n initialFocus={-1}\n returnFocus={true}\n >\n <div\n ref={refs.setFloating}\n data-status={status}\n data-testid=\"drawer\"\n aria-modal\n role=\"dialog\"\n aria-labelledby={id}\n onTransitionEnd={handleDialogVisibility}\n className={clsx(styles.container, className)}\n {...getFloatingProps()}\n {...otherProps}\n >\n <div\n ref={headerRef}\n className={clsx(\n styles.header,\n stackCallToAction && styles.headerStacked\n )}\n >\n <Typography.HeadingMD\n as=\"h2\"\n className={styles.heading}\n id={id}\n ref={initialFocusRef}\n tabIndex={-1}\n weight=\"bold\"\n >\n {heading}\n </Typography.HeadingMD>\n {customCallToAction ? (\n <div\n className={clsx(\n styles.callToAction,\n stackCallToAction &&\n styles.callToActionStacked\n )}\n >\n {customCallToAction}\n </div>\n ) : null}\n </div>\n <div className={styles.content}>{children}</div>\n {/* Rendered last so assistive tech reaches the heading\n and content before the close button; centred against\n the header via its measured height. */}\n <ClickableIcon\n aria-label=\"Close drawer\"\n onClick={onClose}\n focusHighlight={false}\n className={styles.closeButton}\n style={\n closeButtonTop !== undefined\n ? {\n top: closeButtonTop,\n transform: \"translateY(-50%)\",\n }\n : undefined\n }\n >\n <CrossIcon aria-hidden />\n </ClickableIcon>\n </div>\n </FloatingFocusManager>\n ) : undefined}\n </Overlay>\n );\n};\n"],"names":["Drawer","_a","children","heading","show","onClose","onOverlayClick","customCallToAction","className","otherProps","__rest","showOverlay","setShowOverlay","useState","closeButtonTop","setCloseButtonTop","id","useId","initialFocusRef","useRef","width","headerWidth","ref","headerRef","useResizeDetector","refreshMode","refreshRate","onResize","headingEl","current","offsetTop","offsetHeight","stackWidth","parsePxOrRemValue","useResolvedBreakpointToken","Breakpoint","stackCallToAction","undefined","context","refs","useFloating","open","onOpenChange","dismiss","useDismiss","escapeKey","outsidePress","getFloatingProps","useInteractions","isMounted","status","useTransitionStatus","duration","useEffect","timer","setTimeout","clearTimeout","_jsx","Overlay","enableOverlayClick","FloatingFocusManager","initialFocus","returnFocus","_jsxs","Object","assign","setFloating","role","onTransitionEnd","e","propertyName","focus","clsx","styles.container","styles.header","styles.headerStacked","Typography","HeadingMD","as","styles.heading","tabIndex","weight","styles.callToAction","styles.callToActionStacked","styles.content","ClickableIcon","onClick","focusHighlight","styles.closeButton","style","top","transform","CrossIcon"],"mappings":"64CAiCO,MAAMA,EAAUC,IAAA,IAAAC,SACnBA,UACAC,EAAOC,KACPA,EAAIC,QACJA,EAAOC,eACPA,EAAcC,mBACdA,EAAkBC,UAClBA,GAASP,EACNQ,EAAUC,EAAAT,EARM,2FAanB,MAAOU,EAAaC,GAAkBC,EAAST,IAGxCU,EAAgBC,GAAqBF,IACtCG,EAAKC,IACLC,EAAkBC,EAA2B,OAM3CC,MAAOC,EAAaC,IAAKC,GAC7BC,EAAkC,CAC9BC,YAAa,WACbC,YAAa,IACbC,SAAU,KACN,MAAMC,EAAYV,EAAgBW,QAC9BD,GACAb,EACIa,EAAUE,UAAYF,EAAUG,aAAe,EAEvD,IAKNC,EAAaC,EACfC,EAA2BC,EAAW,YAEpCC,OACcC,IAAhBhB,GAA6BA,GAAeW,GAK1CM,QAAEA,EAAOC,KAAEA,GAASC,EAAY,CAClCC,KAAMrC,EACNsC,aAAeD,KACNA,GAAQpC,GACTA,GACJ,IAIFsC,EAAUC,EAAWN,EAAS,CAChCO,WAAW,EACXC,cAAc,KAGZC,iBAAEA,GAAqBC,EAAgB,CAACL,KAExCM,UAAEA,EAASC,OAAEA,GAAWC,EAAoBb,EAAS,CACvDc,SAAU,MAMdC,GAAU,KACN,IAAKjD,EAAM,CACP,MAAMkD,EAAQC,YAAW,IAAM3C,GAAe,IAAQ,KACtD,MAAO,IAAM4C,aAAaF,EAC9B,CACI1C,GAAe,EACnB,GACD,CAACR,IAeJ,OACIqD,EAACC,EAAO,CACJtD,KAAMO,EACNgD,oBAAkB,EAClBrD,eAAgBA,WAEf2C,EACGQ,EAACG,EAAoB,CACjBtB,QAASA,EACTuB,gBACAC,aAAa,EAAI5D,SAEjB6D,EAAA,MAAAC,OAAAC,OAAA,CACI3C,IAAKiB,EAAK2B,YAAW,cACRhB,EAAM,cACP,SAAQ,cAAA,EAEpBiB,KAAK,2BACYnD,EACjBoD,gBA7BYC,UACL,eAAnBA,EAAEC,cAAiClE,IAEZ,QAAvBH,EAAAiB,EAAgBW,eAAO,IAAA5B,GAAAA,EAAEsE,QAC7B,EA0BgB/D,UAAWgE,EAAKC,EAAkBjE,IAC9BuC,IACAtC,EAAU,CAAAP,SAAA,CAEd6D,EAAA,MAAA,CACIzC,IAAKC,EACLf,UAAWgE,EACPE,EACAtC,GAAqBuC,GACxBzE,SAAA,CAEDuD,EAACmB,EAAWC,UAAS,CACjBC,GAAG,KACHtE,UAAWuE,EACX/D,GAAIA,EACJM,IAAKJ,EACL8D,UAAU,EACVC,OAAO,OAAM/E,SAEZC,IAEJI,EACGkD,EAAA,MAAA,CACIjD,UAAWgE,EACPU,EACA9C,GACI+C,GACPjF,SAEAK,IAEL,QAERkD,SAAKjD,UAAW4E,EAAclF,SAAGA,IAIjCuD,EAAC4B,EAAa,CAAA,aACC,eACXC,QAASjF,EACTkF,gBAAgB,EAChB/E,UAAWgF,EACXC,WACuBpD,IAAnBvB,EACM,CACI4E,IAAK5E,EACL6E,UAAW,yBAEftD,EAASnC,SAGnBuD,EAACmC,mCAIbvD,GACE"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export declare const container: import("@linaria/core").LinariaClassName;
|
|
2
2
|
export declare const header: import("@linaria/core").LinariaClassName;
|
|
3
|
+
export declare const headerStacked: import("@linaria/core").LinariaClassName;
|
|
4
|
+
export declare const callToAction: import("@linaria/core").LinariaClassName;
|
|
5
|
+
export declare const callToActionStacked: import("@linaria/core").LinariaClassName;
|
|
3
6
|
export declare const closeButton: import("@linaria/core").LinariaClassName;
|
|
4
7
|
export declare const heading: import("@linaria/core").LinariaClassName;
|
|
5
8
|
export declare const content: import("@linaria/core").LinariaClassName;
|
package/drawer/drawer.styles.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import"./drawer.
|
|
1
|
+
import"./drawer.styles_a4bwe0.css";const c="c1agands",r="h1kc1wos",s="h1njxz6r",a="ccrz6mg",e="c2k10hw",o="cu0iil5",t="hdpobta",w="csm06gm";export{a as callToAction,e as callToActionStacked,o as closeButton,c as container,w as content,r as header,s as headerStacked,t as heading};
|
|
2
2
|
//# sourceMappingURL=drawer.styles.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.styles.js","sources":["../../src/drawer/drawer.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport {\n Border,\n Colour,\n Font,\n MediaQuery,\n Motion,\n Radius,\n Shadow,\n Spacing,\n} from \"../theme\";\n\nexport const container = css`\n position: fixed;\n top: 0;\n\n display: flex;\n flex-direction: column;\n\n height: 100%;\n width: 40%;\n overflow: hidden;\n\n background-color: ${Colour[\"bg\"]};\n box-shadow: ${Shadow[\"lg-subtle\"]};\n border-top-left-radius: ${Radius[\"md\"]};\n border-bottom-left-radius: ${Radius[\"md\"]};\n\n transition-property: right, visibility;\n\n &[data-status=\"initial\"] {\n right: -100%;\n visibility: hidden;\n }\n\n &[data-status=\"open\"] {\n transition-duration: ${Motion[\"duration-800\"]};\n transition-timing-function: ${Motion[\"ease-entrance\"]};\n right: 0;\n visibility: visible;\n }\n\n &[data-status=\"close\"] {\n transition-duration: ${Motion[\"duration-800\"]};\n transition-timing-function: ${Motion[\"ease-exit\"]};\n right: -100%;\n visibility: hidden;\n }\n\n ${MediaQuery.MaxWidth.xl} {\n width: 50%;\n min-width: 700px;\n }\n\n ${MediaQuery.MaxWidth.lg} {\n width: 100%;\n min-width: unset;\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n`;\n\nexport const header = css`\n top: 0;\n display: flex;\n align-items: center;\n gap: ${Spacing[\"spacing-16\"]};\n padding: ${Spacing[\"spacing-32\"]} ${Spacing[\"spacing-16\"]}\n ${Spacing[\"spacing-16\"]}\n calc(${Font.Spec[\"heading-lh-md\"]} + ${Spacing[\"spacing-32\"]});\n background-color: ${Colour[\"bg\"]};\n border-bottom: ${Border[\"width-010\"]} ${Border[\"solid\"]} ${Colour[\"border\"]};\n\n ${MediaQuery.MaxWidth.lg} {\n gap: ${Spacing[\"spacing-8\"]};\n padding: ${Spacing[\"spacing-32\"]} ${Spacing[\"spacing-20\"]}\n ${Spacing[\"spacing-16\"]}\n calc(${Font.Spec[\"heading-lh-md\"]} + ${Spacing[\"spacing-24\"]});\n }\n`;\n\nexport const closeButton = css`\n color: ${Colour[\"icon\"]};\n padding: 0;\n position: absolute;\n top: ${Spacing[\"spacing-32\"]};\n left: ${Spacing[\"spacing-16\"]};\n &:active,\n &:focus {\n color: ${Colour[\"icon-hover\"]};\n }\n\n svg {\n height: ${Font.Spec[\"heading-lh-md\"]};\n width: ${Font.Spec[\"heading-lh-md\"]};\n }\n`;\n\nexport const heading = css`\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n`;\n\nexport const content = css`\n flex: 1;\n overflow-y: auto;\n`;\n"],"names":["container","header","closeButton","heading","content"],"mappings":"yDAaO,MAAMA,EAAS,WAkDTC,EAAM,
|
|
1
|
+
{"version":3,"file":"drawer.styles.js","sources":["../../src/drawer/drawer.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport {\n Border,\n Colour,\n Font,\n MediaQuery,\n Motion,\n Radius,\n Shadow,\n Spacing,\n} from \"../theme\";\n\nexport const container = css`\n position: fixed;\n top: 0;\n\n display: flex;\n flex-direction: column;\n\n height: 100%;\n width: 40%;\n overflow: hidden;\n\n background-color: ${Colour[\"bg\"]};\n box-shadow: ${Shadow[\"lg-subtle\"]};\n border-top-left-radius: ${Radius[\"md\"]};\n border-bottom-left-radius: ${Radius[\"md\"]};\n\n transition-property: right, visibility;\n\n &[data-status=\"initial\"] {\n right: -100%;\n visibility: hidden;\n }\n\n &[data-status=\"open\"] {\n transition-duration: ${Motion[\"duration-800\"]};\n transition-timing-function: ${Motion[\"ease-entrance\"]};\n right: 0;\n visibility: visible;\n }\n\n &[data-status=\"close\"] {\n transition-duration: ${Motion[\"duration-800\"]};\n transition-timing-function: ${Motion[\"ease-exit\"]};\n right: -100%;\n visibility: hidden;\n }\n\n ${MediaQuery.MaxWidth.xl} {\n width: 50%;\n min-width: 700px;\n }\n\n ${MediaQuery.MaxWidth.lg} {\n width: 100%;\n min-width: unset;\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n`;\n\nexport const header = css`\n top: 0;\n display: flex;\n align-items: center;\n gap: ${Spacing[\"spacing-16\"]};\n padding: ${Spacing[\"spacing-32\"]} ${Spacing[\"spacing-16\"]}\n ${Spacing[\"spacing-16\"]}\n calc(${Font.Spec[\"heading-lh-md\"]} + ${Spacing[\"spacing-32\"]});\n background-color: ${Colour[\"bg\"]};\n border-bottom: ${Border[\"width-010\"]} ${Border[\"solid\"]} ${Colour[\"border\"]};\n\n ${MediaQuery.MaxWidth.lg} {\n gap: ${Spacing[\"spacing-8\"]};\n padding: ${Spacing[\"spacing-32\"]} ${Spacing[\"spacing-20\"]}\n ${Spacing[\"spacing-16\"]}\n calc(${Font.Spec[\"heading-lh-md\"]} + ${Spacing[\"spacing-24\"]});\n }\n`;\n\n// Stack the call-to-action below the heading when the drawer is too narrow\n// to fit both on one line.\nexport const headerStacked = css`\n flex-direction: column;\n align-items: stretch;\n gap: ${Spacing[\"spacing-16\"]};\n`;\n\nexport const callToAction = css`\n display: flex;\n margin-left: auto;\n`;\n\n// When stacked, flush left so the buttons line up with the heading (the\n// header's left padding already clears the close icon).\nexport const callToActionStacked = css`\n margin-left: 0;\n`;\n\nexport const closeButton = css`\n color: ${Colour[\"icon\"]};\n padding: 0;\n position: absolute;\n top: ${Spacing[\"spacing-32\"]};\n left: ${Spacing[\"spacing-16\"]};\n &:active,\n &:focus {\n color: ${Colour[\"icon-hover\"]};\n }\n\n svg {\n height: ${Font.Spec[\"heading-lh-md\"]};\n width: ${Font.Spec[\"heading-lh-md\"]};\n }\n`;\n\nexport const heading = css`\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n`;\n\nexport const content = css`\n flex: 1;\n overflow-y: auto;\n`;\n"],"names":["container","header","headerStacked","callToAction","callToActionStacked","closeButton","heading","content"],"mappings":"yDAaO,MAAMA,EAAS,WAkDTC,EAAM,WAqBNC,EAAa,WAMbC,EAAY,UAOZC,EAAmB,UAInBC,EAAW,UAiBXC,EAAO,UAMPC,EAAO"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
@layer fds{.c1agands{position:fixed;top:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;height:100%;width:40%;overflow:hidden;background-color:var(--fds-colour-bg);box-shadow:var(--fds-shadow-lg-subtle);border-top-left-radius:var(--fds-radius-md);border-bottom-left-radius:var(--fds-radius-md);transition-property:right,visibility;}.c1agands[data-status="initial"]{right:-100%;visibility:hidden;}.c1agands[data-status="open"]{transition-duration:var(--fds-motion-duration-800);transition-timing-function:var(--fds-motion-timing-ease-entrance);right:0;visibility:visible;}.c1agands[data-status="close"]{transition-duration:var(--fds-motion-duration-800);transition-timing-function:var(--fds-motion-timing-ease-exit);right:-100%;visibility:hidden;}:where(body.fds-breakpoint-xl-max) .c1agands{width:50%;min-width:700px;}:where(body.fds-breakpoint-lg-max) .c1agands{width:100%;min-width:unset;border-top-left-radius:0;border-bottom-left-radius:0;}
|
|
2
2
|
.h1kc1wos{top:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:var(--fds-spacing-16);padding:var(--fds-spacing-32) var(--fds-spacing-16) var(--fds-spacing-16) calc(var(--fds-font-heading-lh-md) + var(--fds-spacing-32));background-color:var(--fds-colour-bg);border-bottom:var(--fds-border-width-010) var(--fds-border-style-solid) var(--fds-colour-border);}:where(body.fds-breakpoint-lg-max) .h1kc1wos{gap:var(--fds-spacing-8);padding:var(--fds-spacing-32) var(--fds-spacing-20) var(--fds-spacing-16) calc(var(--fds-font-heading-lh-md) + var(--fds-spacing-24));}
|
|
3
|
-
.
|
|
4
|
-
.
|
|
5
|
-
.c2k10hw{-
|
|
3
|
+
.h1njxz6r{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:stretch;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;gap:var(--fds-spacing-16);}
|
|
4
|
+
.ccrz6mg{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin-left:auto;}
|
|
5
|
+
.c2k10hw{margin-left:0;}
|
|
6
|
+
.cu0iil5{color:var(--fds-colour-icon);padding:0;position:absolute;top:var(--fds-spacing-32);left:var(--fds-spacing-16);}.cu0iil5:active,.cu0iil5:focus{color:var(--fds-colour-icon-hover);}.cu0iil5 svg{height:var(--fds-font-heading-lh-md);width:var(--fds-font-heading-lh-md);}
|
|
7
|
+
.hdpobta{text-overflow:ellipsis;overflow:hidden;white-space:nowrap;}
|
|
8
|
+
.csm06gm{-webkit-flex:1;-ms-flex:1;flex:1;overflow-y:auto;}}
|
package/drawer/types.d.ts
CHANGED
|
@@ -23,4 +23,11 @@ export interface DrawerProps {
|
|
|
23
23
|
onClose?: (() => void) | undefined;
|
|
24
24
|
/** Called when the user clicks the background overlay. */
|
|
25
25
|
onOverlayClick?: (() => void) | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Optional element rendered in the header, e.g. a link or button.
|
|
28
|
+
*
|
|
29
|
+
* Sits beside the title on wider drawers, and wraps onto its own line
|
|
30
|
+
* below the title once the drawer is narrow.
|
|
31
|
+
*/
|
|
32
|
+
customCallToAction?: React.ReactNode | undefined;
|
|
26
33
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as e,jsxs as r,Fragment as t}from"react/jsx-runtime";import{useSensors as i,useSensor as s,DndContext as o,KeyboardSensor as n,TouchSensor as l}from"../../external/@dnd-kit/core/dist/core.esm.js";import{SortableContext as
|
|
1
|
+
import{jsx as e,jsxs as r,Fragment as t}from"react/jsx-runtime";import{useSensors as i,useSensor as s,DndContext as o,KeyboardSensor as n,TouchSensor as l}from"../../external/@dnd-kit/core/dist/core.esm.js";import{SortableContext as a,verticalListSortingStrategy as d,arrayMove as c,sortableKeyboardCoordinates as p}from"../../external/@dnd-kit/sortable/dist/sortable.esm.js";import m from"clsx";import{forwardRef as f,useContext as u,useState as g,useRef as h,useImperativeHandle as b,useEffect as v}from"react";import{useResizeDetector as j}from"../../external/react-resize-detector/build/index.esm.js";import{VisuallyHidden as x}from"../../shared/accessibility/index.js";import{FileUploadContext as y}from"../context.js";import{MouseSensor as $}from"../custom-sensors.js";import{FileListItem as I}from"../file-list-item/file-list-item.js";import{FileUploadHelper as M}from"../helper.js";import{listWrapper as D,listWrapperReadOnly as S}from"./file-list.styles.js";const O=f((function({fileItems:f=[],editableFileItems:O,fileDescriptionMaxLength:w,descriptionRequired:C,descriptionLabel:L,sortable:E,disabled:F,readOnly:R,onItemUpdate:k,onItemDelete:q,onSort:B},N){const{setActiveId:z}=u(y),[A,G]=g(0),[K,T]=g(""),U=h({});b(N,(()=>({focus:()=>{var e;null===(e=H.current)||void 0===e||e.focus()}})));const{width:W=0,ref:H}=j(),J=h({}),P=i(s($,{activationConstraint:{distance:8}}),s(l,{activationConstraint:{delay:150,tolerance:5}}),s(n,{coordinateGetter:p})),Q=e=>{delete J.current[e]};v((()=>{var e;if(!f||0===f.length)return void T("");const r=Date.now(),t=[];for(const i of f){const s=U.current[i.id];if(i.errorMessage)"error"!==(null==s?void 0:s.status)&&(t.push("string"==typeof i.errorMessage?`Error uploading ${i.name}: ${i.errorMessage}`:`Error uploading ${i.name}`),U.current[i.id]={progress:null!==(e=i.progress)&&void 0!==e?e:1,timestamp:r,status:"error"});else if(void 0!==i.progress)if(i.progress>=1)"complete"!==(null==s?void 0:s.status)&&(t.push(`${i.name} upload is complete`),U.current[i.id]={progress:1,timestamp:r,status:"complete"});else if("number"==typeof i.progress&&i.progress<1){(!s||"in-progress"!==s.status)&&(t.push(`Starting upload of ${i.name}`),U.current[i.id]={progress:i.progress,timestamp:r,status:"in-progress"})}}t.length>0&&T(t.join(", "))}),[f]);const V=e=>r=>{Q(e.id);const t=Object.assign(Object.assign({},e),{description:r});k(t)},X=e=>r=>{J.current[e.id]=r},Y=e=>()=>{Q(e.id),e.description&&0!==e.description.length||q(e)},Z=e=>{G((r=>"edit"===e?r+1:Math.max(0,r-1)))},_=e=>()=>{q(e),H.current&&H.current.focus()},ee=e=>O&&M.isSupportedImageType(e.type),re=()=>f&&f.length>1&&E&&!R&&!(A>0||f.some((e=>!!e.errorMessage))),te=()=>{if(0===f.length)return"No files uploaded";const e=f.filter((e=>!(null==e?void 0:e.errorMessage)&&(1===e.progress||void 0===e.progress))).length,r=f.filter((e=>"number"==typeof e.progress&&e.progress<1)).length,t=f.filter((e=>e.errorMessage)).length;if(0===e&&0===r&&0===t)return"File list";const i=[e>0?`${e} completed files`:"",r>0?`${r} file in progress`:"",t>0?`${t} ${t>1?"files":"file"} with error`:""].filter(Boolean);return`${R?"Read-only file list":"File list"}. ${i.join(", ")}`};return e(o,{sensors:P,onDragEnd:e=>{if(B&&f){const{active:r,over:t}=e;if(t&&r.id!==t.id){const e=f.findIndex((e=>e.id===r.id)),i=f.findIndex((e=>e.id===t.id)),s=c(f,e,i);B(s)}}z(void 0)},onDragStart:e=>{const{active:r}=e;z(r.id)},children:e(a,{items:f,strategy:d,children:r(t,{children:[e(x,{"aria-live":"polite","aria-atomic":"true",children:K}),e("ul",{tabIndex:-1,className:m(D,R&&S),ref:H,"aria-label":te(),children:0===f.length?null:f.map((r=>{const t=Object.assign({},r);return void 0!==J.current[r.id]&&(t.description=J.current[r.id]),e(I,{fileItem:t,editable:ee(r),wrapperWidth:W,sortable:re(),disabled:F,readOnly:R,descriptionLabel:L,fileDescriptionMaxLength:w,descriptionRequired:C,onDelete:_(r),onSave:V(r),onCancel:Y(r),onBlur:X(r),onModeChange:Z},r.id)}))})]})})})}));export{O as FileList};
|
|
2
2
|
//# sourceMappingURL=file-list.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-list.js","sources":["../../../src/file-upload/file-list/file-list.tsx"],"sourcesContent":["import type { DragEndEvent, DragStartEvent } from \"@dnd-kit/core\";\nimport {\n DndContext,\n KeyboardSensor,\n TouchSensor,\n useSensor,\n useSensors,\n} from \"@dnd-kit/core\";\nimport {\n arrayMove,\n SortableContext,\n sortableKeyboardCoordinates,\n verticalListSortingStrategy,\n} from \"@dnd-kit/sortable\";\nimport clsx from \"clsx\";\nimport type React from \"react\";\nimport {\n forwardRef,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\nimport { useResizeDetector } from \"react-resize-detector\";\n\nimport type { FormLabelProps } from \"../../form/form-label/types\";\nimport { VisuallyHidden } from \"../../shared/accessibility\";\nimport { FileUploadContext } from \"../context\";\nimport { MouseSensor } from \"../custom-sensors\";\nimport { FileItemEdit } from \"../file-item-edit\";\nimport { FileListItem } from \"../file-list-item\";\nimport { FileUploadHelper } from \"../helper\";\nimport type { FileItemProps } from \"../types\";\nimport * as styles from \"./file-list.styles\";\n\n// =============================================================================\n// INTERFACES\n// =============================================================================\ntype RenderMode = \"edit\" | \"display\" | \"error\";\n\ntype FileItemRenderModes = Record<string, RenderMode>;\ntype FileEditedDescriptions = Record<string, string>;\n\ntype RenderItem = FileItemProps | FileItemProps[];\n\ninterface Props {\n fileItems: FileItemProps[] | undefined;\n editableFileItems: boolean;\n fileDescriptionMaxLength?: number | undefined;\n descriptionRequired?: boolean | undefined;\n descriptionLabel?: FormLabelProps | undefined;\n sortable?: boolean | undefined;\n disabled?: boolean | undefined;\n readOnly?: boolean | undefined;\n onItemUpdate: (item: FileItemProps) => void;\n onItemDelete: (item: FileItemProps) => void;\n onSort?: ((reorderedFileItems: FileItemProps[]) => void) | undefined;\n}\n\ntype AnnouncementProgress = Record<\n string,\n {\n progress: number;\n timestamp: number;\n status: \"in-progress\" | \"complete\" | \"error\";\n }\n>;\n\nexport interface FileListRef {\n focus: () => void;\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\nfunction Component(\n {\n fileItems = [],\n editableFileItems,\n fileDescriptionMaxLength,\n descriptionRequired,\n descriptionLabel,\n sortable,\n disabled,\n readOnly,\n onItemUpdate,\n onItemDelete,\n onSort,\n }: Props,\n ref: React.Ref<FileListRef>\n) {\n // =========================================================================\n // CONST, STATE, REFS\n // =========================================================================\n const [renderModes, setRenderModes] = useState<FileItemRenderModes>({});\n const { setActiveId } = useContext(FileUploadContext);\n\n // Progress announcement state (for aria-live) - announces start and completion only\n const [progressAnnouncement, setProgressAnnouncement] = useState(\"\");\n const lastAnnouncedRef = useRef<AnnouncementProgress>({});\n\n useImperativeHandle(ref, () => ({\n focus: () => {\n wrapperRef.current?.focus();\n },\n }));\n const { width: wrapperWidth = 0, ref: wrapperRef } = useResizeDetector();\n\n // Keep track of edited description without re-rendering\n const descriptionsValueRef = useRef<FileEditedDescriptions>({});\n\n /**\n * As the default drag sensors interfere with click events\n * on the file items, we'll need to configure the sensor to\n * only activate the drag if the mouse moves a certain distance\n */\n const sensors = useSensors(\n useSensor(MouseSensor, {\n activationConstraint: {\n distance: 8, // mouse drag of 8px then activate the drag event\n },\n }),\n useSensor(TouchSensor, {\n activationConstraint: {\n delay: 150,\n tolerance: 5,\n },\n }),\n useSensor(KeyboardSensor, {\n coordinateGetter: sortableKeyboardCoordinates,\n })\n );\n\n // =========================================================================\n // REF METHODS\n // =========================================================================\n const removeDescription = (itemId: string) => {\n delete descriptionsValueRef.current[itemId];\n };\n\n const shallowCompareRenderModes = (\n prev: FileItemRenderModes,\n next: FileItemRenderModes\n ) => {\n const prevKeys = Object.keys(prev);\n const nextKeys = Object.keys(next);\n if (prevKeys.length !== nextKeys.length) {\n return false;\n }\n for (const k of nextKeys) {\n if (prev[k] !== next[k]) {\n return false;\n }\n }\n return true;\n };\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n useEffect(() => {\n const nextModes = getItemsRenderMode(fileItems);\n const prevModes = renderModes;\n // We perform shallowCompare to avoid infinite re-render loop\n if (shallowCompareRenderModes(prevModes, nextModes)) {\n return;\n }\n setRenderModes(nextModes);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [fileItems, editableFileItems, readOnly]);\n\n // Progress announcements only at start and completion\n useEffect(() => {\n if (!fileItems || fileItems.length === 0) {\n setProgressAnnouncement(\"\");\n return;\n }\n const now = Date.now();\n const messages: string[] = [];\n\n for (const item of fileItems) {\n const fileStatus = lastAnnouncedRef.current[item.id];\n\n if (item.errorMessage) {\n if (fileStatus?.status !== \"error\") {\n messages.push(\n typeof item.errorMessage === \"string\"\n ? `Error uploading ${item.name}: ${item.errorMessage}`\n : `Error uploading ${item.name}`\n );\n lastAnnouncedRef.current[item.id] = {\n progress: item.progress ?? 1,\n timestamp: now,\n status: \"error\",\n };\n }\n continue;\n }\n\n if (item.progress === undefined) {\n continue;\n }\n\n if (item.progress >= 1) {\n if (fileStatus?.status !== \"complete\") {\n messages.push(`${item.name} upload is complete`);\n lastAnnouncedRef.current[item.id] = {\n progress: 1,\n timestamp: now,\n status: \"complete\",\n };\n }\n continue;\n }\n\n // In progress - only announce start, not progress updates\n if (typeof item.progress === \"number\" && item.progress < 1) {\n // Only announce when starting upload (no previous status or status was not in-progress)\n const shouldAnnounce =\n !fileStatus || fileStatus.status !== \"in-progress\";\n\n if (shouldAnnounce) {\n messages.push(`Starting upload of ${item.name}`);\n lastAnnouncedRef.current[item.id] = {\n progress: item.progress,\n timestamp: now,\n status: \"in-progress\",\n };\n }\n }\n }\n\n if (messages.length > 0) {\n // Update aria-live region once with aggregated message\n setProgressAnnouncement(messages.join(\", \"));\n }\n }, [fileItems]);\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleSaveEdit = (item: FileItemProps) => (description: string) => {\n updateRenderModes(item.id, \"display\");\n removeDescription(item.id);\n\n const updatedItem = { ...item, description };\n onItemUpdate(updatedItem);\n };\n\n const handleBlurEdit = (item: FileItemProps) => (value: string) => {\n descriptionsValueRef.current[item.id] = value;\n };\n\n const handleCancel = (item: FileItemProps) => () => {\n if (!item.description || item.description.length === 0) {\n // New addition\n onItemDelete(item);\n } else {\n updateRenderModes(item.id, \"display\");\n }\n removeDescription(item.id);\n };\n\n const handleInitiateEdit = (item: FileItemProps) => () => {\n updateRenderModes(item.id, \"edit\");\n };\n\n const handleDelete = (item: FileItemProps) => () => {\n onItemDelete(item);\n if (wrapperRef.current) {\n wrapperRef.current.focus();\n }\n };\n\n const handleDragEnd = (event: DragEndEvent) => {\n if (onSort && fileItems) {\n const { active, over } = event;\n\n if (over && active.id !== over.id) {\n const oldIndex = fileItems.findIndex(\n (item) => item.id === active.id\n );\n const newIndex = fileItems.findIndex(\n (item) => item.id === over.id\n );\n\n const updatedFileItems = arrayMove(\n fileItems,\n oldIndex,\n newIndex\n );\n\n onSort(updatedFileItems);\n }\n }\n\n setActiveId(undefined);\n };\n\n const handleDragStart = (event: DragStartEvent) => {\n const { active } = event;\n setActiveId(active.id as string);\n };\n\n // =========================================================================\n // HELPER FUNCTIONS\n // =========================================================================\n const checkEditable = (item: FileItemProps) => {\n return (\n editableFileItems &&\n FileUploadHelper.isSupportedImageType(item.type)\n );\n };\n\n const shouldRenderEditMode = (item: FileItemProps) => {\n return (\n !item.errorMessage &&\n !readOnly &&\n !(typeof item.progress === \"number\" && item.progress < 1) &&\n checkEditable(item) &&\n !item.description\n );\n };\n\n const getItemsRenderMode = (\n fileItems: FileItemProps[] | undefined\n ): FileItemRenderModes => {\n if (!fileItems || fileItems.length === 0) return {};\n\n const newRenderModes: FileItemRenderModes = {};\n\n for (const item of fileItems) {\n if (item.errorMessage) {\n newRenderModes[item.id] = \"error\";\n } else if (!renderModes[item.id]) {\n newRenderModes[item.id] = shouldRenderEditMode(item)\n ? \"edit\"\n : \"display\";\n } else {\n newRenderModes[item.id] = renderModes[item.id];\n }\n }\n\n return newRenderModes;\n };\n\n const updateRenderModes = (itemId: string, mode: RenderMode) => {\n setRenderModes((prevRenderModes) => {\n return {\n ...prevRenderModes,\n [itemId]: mode,\n };\n });\n };\n\n /**\n * Due to a UI requirement, we will render the items\n * with edit modes as a group\n */\n const getArrangedItems = (\n fileItems: FileItemProps[] | undefined,\n renderModes: FileItemRenderModes\n ) => {\n if (!fileItems || fileItems.length === 0) return [];\n\n const arrangedItems: RenderItem[] = [];\n\n for (const fileItem of fileItems) {\n if (renderModes[fileItem.id] === \"edit\") {\n const previousElement = arrangedItems[arrangedItems.length - 1];\n if (Array.isArray(previousElement)) {\n previousElement.push(fileItem);\n } else {\n arrangedItems.push([fileItem]);\n }\n } else {\n arrangedItems.push(fileItem);\n }\n }\n\n return arrangedItems;\n };\n\n const areAllItemsInDisplayViews = () => {\n return Object.values(renderModes).every((mode) => mode === \"display\");\n };\n\n const shouldEnableSort = () => {\n return (\n fileItems &&\n fileItems.length > 1 &&\n sortable &&\n areAllItemsInDisplayViews()\n );\n };\n\n /**\n * If there are no files, return \"No files uploaded\".\n * If readOnly is true, return \"Read-only file list\".\n * If there are files, return a summary of the file statuses.\n * For example, \"File list. 2 completed files, 1 file in progress, 1 file with error\".\n *\n * @returns aria label\n */\n const getWrapperAriaLabel = () => {\n if (fileItems.length === 0) {\n return \"No files uploaded\";\n }\n const completedCount = fileItems.filter(\n (item) =>\n !item?.errorMessage &&\n (item.progress === 1 || item.progress === undefined)\n ).length;\n const inProgressCount = fileItems.filter(\n (item) => typeof item.progress === \"number\" && item.progress < 1\n ).length;\n const errorCount = fileItems.filter((item) => item.errorMessage).length;\n if (completedCount === 0 && inProgressCount === 0 && errorCount === 0) {\n return `File list`;\n }\n const completedText =\n completedCount > 0 ? `${completedCount} completed files` : \"\";\n const inProgressText =\n inProgressCount > 0 ? `${inProgressCount} file in progress` : \"\";\n const errorText =\n errorCount > 0\n ? `${errorCount} ${\n errorCount > 1 ? \"files\" : \"file\"\n } with error`\n : \"\";\n const parts = [completedText, inProgressText, errorText].filter(\n Boolean\n );\n const prefix = readOnly ? \"Read-only file list\" : \"File list\";\n return `${prefix}. ${parts.join(\", \")}`;\n };\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n const renderItemsInEditMode = (\n fileItems: FileItemProps[],\n keyToUse: number\n ) => {\n const itemsToRender = fileItems.map((item) => {\n const updatedFileItem = { ...item };\n if (descriptionsValueRef.current[item.id] !== undefined) {\n updatedFileItem.description =\n descriptionsValueRef.current[item.id];\n }\n\n return (\n <FileItemEdit\n key={item.id}\n fileItem={updatedFileItem}\n wrapperWidth={wrapperWidth}\n fileDescriptionMaxLength={fileDescriptionMaxLength}\n descriptionRequired={descriptionRequired}\n descriptionLabel={descriptionLabel}\n onSave={handleSaveEdit(item)}\n onCancel={handleCancel(item)}\n onBlur={handleBlurEdit(item)}\n />\n );\n });\n\n return (\n <li\n key={`editable-${keyToUse}`}\n className={styles.editableItemsContainer}\n >\n <ul>{itemsToRender}</ul>\n </li>\n );\n };\n\n const renderItems = () => {\n const arrangedItems = getArrangedItems(fileItems, renderModes);\n\n if (arrangedItems.length === 0) return null;\n\n return arrangedItems.map((item, index) => {\n if (Array.isArray(item)) {\n return renderItemsInEditMode(item, index);\n } else {\n return (\n <FileListItem\n key={item.id}\n fileItem={item}\n editable={checkEditable(item)}\n wrapperWidth={wrapperWidth}\n sortable={shouldEnableSort()}\n disabled={disabled}\n readOnly={readOnly}\n descriptionLabel={descriptionLabel}\n onDelete={handleDelete(item)}\n onEditClick={handleInitiateEdit(item)}\n />\n );\n }\n });\n };\n\n /**\n * Renders the progress status of each file upload.\n * In progress: 25% complete for uploading filename.pdf\n * Complete: filename.pdf upload is complete\n * Error: Error uploading filename.pdf: error message\n *\n * @returns A visually hidden element announcing the progress status.\n */\n const renderProgressStatus = () => (\n <VisuallyHidden aria-live=\"polite\" aria-atomic=\"true\">\n {progressAnnouncement}\n </VisuallyHidden>\n );\n\n const renderItemsWithWrapper = () => {\n return (\n <>\n {renderProgressStatus()}\n <ul\n tabIndex={-1}\n className={clsx(\n styles.listWrapper,\n readOnly && styles.listWrapperReadOnly\n )}\n ref={wrapperRef}\n aria-label={getWrapperAriaLabel()}\n >\n {renderItems()}\n </ul>\n </>\n );\n };\n\n if (disabled || readOnly || !shouldEnableSort()) {\n return renderItemsWithWrapper();\n } else {\n return (\n <DndContext\n sensors={sensors}\n onDragEnd={handleDragEnd}\n onDragStart={handleDragStart}\n >\n <SortableContext\n items={fileItems}\n strategy={verticalListSortingStrategy}\n >\n {renderItemsWithWrapper()}\n </SortableContext>\n </DndContext>\n );\n }\n}\n\nexport const FileList = forwardRef<FileListRef, Props>(Component);\n"],"names":["FileList","forwardRef","fileItems","editableFileItems","fileDescriptionMaxLength","descriptionRequired","descriptionLabel","sortable","disabled","readOnly","onItemUpdate","onItemDelete","onSort","ref","renderModes","setRenderModes","useState","setActiveId","useContext","FileUploadContext","progressAnnouncement","setProgressAnnouncement","lastAnnouncedRef","useRef","useImperativeHandle","focus","_a","wrapperRef","current","width","wrapperWidth","useResizeDetector","descriptionsValueRef","sensors","useSensors","useSensor","MouseSensor","activationConstraint","distance","TouchSensor","delay","tolerance","KeyboardSensor","coordinateGetter","sortableKeyboardCoordinates","removeDescription","itemId","useEffect","nextModes","getItemsRenderMode","prev","next","prevKeys","Object","keys","nextKeys","length","k","shallowCompareRenderModes","now","Date","messages","item","fileStatus","id","errorMessage","status","push","name","progress","timestamp","undefined","join","handleSaveEdit","description","updateRenderModes","updatedItem","assign","handleBlurEdit","value","handleCancel","handleInitiateEdit","handleDelete","handleDragEnd","event","active","over","oldIndex","findIndex","newIndex","updatedFileItems","arrayMove","handleDragStart","checkEditable","FileUploadHelper","isSupportedImageType","type","shouldRenderEditMode","newRenderModes","mode","prevRenderModes","shouldEnableSort","values","every","getWrapperAriaLabel","completedCount","filter","inProgressCount","errorCount","parts","Boolean","renderItems","arrangedItems","fileItem","previousElement","Array","isArray","getArrangedItems","map","index","keyToUse","itemsToRender","updatedFileItem","_jsx","FileItemEdit","onSave","onCancel","onBlur","className","styles.editableItemsContainer","children","renderItemsInEditMode","FileListItem","editable","onDelete","onEditClick","renderItemsWithWrapper","_jsxs","_Fragment","VisuallyHidden","tabIndex","clsx","styles.listWrapper","styles.listWrapperReadOnly","DndContext","onDragEnd","onDragStart","SortableContext","items","strategy","verticalListSortingStrategy"],"mappings":"6hCA8iBaA,EAAWC,GAjexB,UACIC,UACIA,EAAY,GAAEC,kBACdA,EAAiBC,yBACjBA,EAAwBC,oBACxBA,EAAmBC,iBACnBA,EAAgBC,SAChBA,EAAQC,SACRA,EAAQC,SACRA,EAAQC,aACRA,EAAYC,aACZA,EAAYC,OACZA,GAEJC,GAKA,MAAOC,EAAaC,GAAkBC,EAA8B,CAAA,IAC9DC,YAAEA,GAAgBC,EAAWC,IAG5BC,EAAsBC,GAA2BL,EAAS,IAC3DM,EAAmBC,EAA6B,IAEtDC,EAAoBX,GAAK,KAAA,CACrBY,MAAO,WACe,QAAlBC,EAAAC,EAAWC,eAAO,IAAAF,GAAAA,EAAED,OAAO,MAGnC,MAAQI,MAAOC,EAAe,EAAGjB,IAAKc,GAAeI,IAG/CC,EAAuBT,EAA+B,IAOtDU,EAAUC,EACZC,EAAUC,EAAa,CACnBC,qBAAsB,CAClBC,SAAU,KAGlBH,EAAUI,EAAa,CACnBF,qBAAsB,CAClBG,MAAO,IACPC,UAAW,KAGnBN,EAAUO,EAAgB,CACtBC,iBAAkBC,KAOpBC,EAAqBC,WAChBd,EAAqBJ,QAAQkB,EAAO,EAuB/CC,GAAU,KACN,MAAMC,EAAYC,GAAmB/C,GArBP,EAC9BgD,EACAC,KAEA,MAAMC,EAAWC,OAAOC,KAAKJ,GACvBK,EAAWF,OAAOC,KAAKH,GAC7B,GAAIC,EAASI,SAAWD,EAASC,OAC7B,OAAO,EAEX,IAAK,MAAMC,KAAKF,EACZ,GAAIL,EAAKO,KAAON,EAAKM,GACjB,OAAO,EAGf,OAAO,CAAI,EAUPC,CAFc5C,EAEuBkC,IAGzCjC,EAAeiC,EAAU,GAE1B,CAAC9C,EAAWC,EAAmBM,IAGlCsC,GAAU,WACN,IAAK7C,GAAkC,IAArBA,EAAUsD,OAExB,YADAnC,EAAwB,IAG5B,MAAMsC,EAAMC,KAAKD,MACXE,EAAqB,GAE3B,IAAK,MAAMC,KAAQ5D,EAAW,CAC1B,MAAM6D,EAAazC,EAAiBM,QAAQkC,EAAKE,IAEjD,GAAIF,EAAKG,aACsB,WAAvBF,aAAU,EAAVA,EAAYG,UACZL,EAASM,KACwB,iBAAtBL,EAAKG,aACN,mBAAmBH,EAAKM,SAASN,EAAKG,eACtC,mBAAmBH,EAAKM,QAElC9C,EAAiBM,QAAQkC,EAAKE,IAAM,CAChCK,iBAAU3C,EAAAoC,EAAKO,wBAAY,EAC3BC,UAAWX,EACXO,OAAQ,eAMpB,QAAsBK,IAAlBT,EAAKO,SAIT,GAAIP,EAAKO,UAAY,EACU,cAAvBN,aAAU,EAAVA,EAAYG,UACZL,EAASM,KAAK,GAAGL,EAAKM,2BACtB9C,EAAiBM,QAAQkC,EAAKE,IAAM,CAChCK,SAAU,EACVC,UAAWX,EACXO,OAAQ,kBAOpB,GAA6B,iBAAlBJ,EAAKO,UAAyBP,EAAKO,SAAW,EAAG,GAGnDN,GAAoC,gBAAtBA,EAAWG,UAG1BL,EAASM,KAAK,sBAAsBL,EAAKM,QACzC9C,EAAiBM,QAAQkC,EAAKE,IAAM,CAChCK,SAAUP,EAAKO,SACfC,UAAWX,EACXO,OAAQ,eAGpB,CACJ,CAEIL,EAASL,OAAS,GAElBnC,EAAwBwC,EAASW,KAAK,MAC1C,GACD,CAACtE,IAKJ,MAAMuE,EAAkBX,GAAyBY,IAC7CC,GAAkBb,EAAKE,GAAI,WAC3BnB,EAAkBiB,EAAKE,IAEvB,MAAMY,EAAWvB,OAAAwB,OAAAxB,OAAAwB,OAAA,CAAA,EAAQf,GAAI,CAAEY,gBAC/BhE,EAAakE,EAAY,EAGvBE,EAAkBhB,GAAyBiB,IAC7C/C,EAAqBJ,QAAQkC,EAAKE,IAAMe,CAAK,EAG3CC,EAAgBlB,GAAwB,KACrCA,EAAKY,aAA2C,IAA5BZ,EAAKY,YAAYlB,OAItCmB,GAAkBb,EAAKE,GAAI,WAF3BrD,EAAamD,GAIjBjB,EAAkBiB,EAAKE,GAAG,EAGxBiB,GAAsBnB,GAAwB,KAChDa,GAAkBb,EAAKE,GAAI,OAAO,EAGhCkB,GAAgBpB,GAAwB,KAC1CnD,EAAamD,GACTnC,EAAWC,SACXD,EAAWC,QAAQH,OACvB,EAGE0D,GAAiBC,IACnB,GAAIxE,GAAUV,EAAW,CACrB,MAAMmF,OAAEA,EAAMC,KAAEA,GAASF,EAEzB,GAAIE,GAAQD,EAAOrB,KAAOsB,EAAKtB,GAAI,CAC/B,MAAMuB,EAAWrF,EAAUsF,WACtB1B,GAASA,EAAKE,KAAOqB,EAAOrB,KAE3ByB,EAAWvF,EAAUsF,WACtB1B,GAASA,EAAKE,KAAOsB,EAAKtB,KAGzB0B,EAAmBC,EACrBzF,EACAqF,EACAE,GAGJ7E,EAAO8E,EACX,CACJ,CAEAzE,OAAYsD,EAAU,EAGpBqB,GAAmBR,IACrB,MAAMC,OAAEA,GAAWD,EACnBnE,EAAYoE,EAAOrB,GAAa,EAM9B6B,GAAiB/B,GAEf3D,GACA2F,EAAiBC,qBAAqBjC,EAAKkC,MAI7CC,GAAwBnC,IAErBA,EAAKG,eACLxD,KAC0B,iBAAlBqD,EAAKO,UAAyBP,EAAKO,SAAW,IACvDwB,GAAc/B,KACbA,EAAKY,YAIRzB,GACF/C,IAEA,IAAKA,GAAkC,IAArBA,EAAUsD,OAAc,MAAO,CAAA,EAEjD,MAAM0C,EAAsC,CAAA,EAE5C,IAAK,MAAMpC,KAAQ5D,EACX4D,EAAKG,aACLiC,EAAepC,EAAKE,IAAM,QAClBlD,EAAYgD,EAAKE,IAKzBkC,EAAepC,EAAKE,IAAMlD,EAAYgD,EAAKE,IAJ3CkC,EAAepC,EAAKE,IAAMiC,GAAqBnC,GACzC,OACA,UAMd,OAAOoC,CAAc,EAGnBvB,GAAoB,CAAC7B,EAAgBqD,KACvCpF,GAAgBqF,GACZ/C,OAAAwB,OAAAxB,OAAAwB,OAAA,CAAA,EACOuB,GAAe,CAClBtD,CAACA,GAASqD,KAEhB,EAmCAE,GAAmB,IAEjBnG,GACAA,EAAUsD,OAAS,GACnBjD,GAPG8C,OAAOiD,OAAOxF,GAAayF,OAAOJ,GAAkB,YAATA,IAoBhDK,GAAsB,KACxB,GAAyB,IAArBtG,EAAUsD,OACV,MAAO,oBAEX,MAAMiD,EAAiBvG,EAAUwG,QAC5B5C,KACIA,eAAAA,EAAMG,gBACY,IAAlBH,EAAKO,eAAoCE,IAAlBT,EAAKO,YACnCb,OACImD,EAAkBzG,EAAUwG,QAC7B5C,GAAkC,iBAAlBA,EAAKO,UAAyBP,EAAKO,SAAW,IACjEb,OACIoD,EAAa1G,EAAUwG,QAAQ5C,GAASA,EAAKG,eAAcT,OACjE,GAAuB,IAAnBiD,GAA4C,IAApBE,GAAwC,IAAfC,EACjD,MAAO,YAEX,MAUMC,EAAQ,CATVJ,EAAiB,EAAI,GAAGA,oBAAmC,GAE3DE,EAAkB,EAAI,GAAGA,qBAAqC,GAE9DC,EAAa,EACP,GAAGA,KACCA,EAAa,EAAI,QAAU,oBAE/B,IAC+CF,OACrDI,SAGJ,MAAO,GADQrG,EAAW,sBAAwB,gBAC7BoG,EAAMrC,KAAK,OAAO,EA0CrCuC,GAAc,KAChB,MAAMC,EAtHe,EACrB9G,EACAY,KAEA,IAAKZ,GAAkC,IAArBA,EAAUsD,OAAc,MAAO,GAEjD,MAAMwD,EAA8B,GAEpC,IAAK,MAAMC,KAAY/G,EACnB,GAAiC,SAA7BY,EAAYmG,EAASjD,IAAgB,CACrC,MAAMkD,EAAkBF,EAAcA,EAAcxD,OAAS,GACzD2D,MAAMC,QAAQF,GACdA,EAAgB/C,KAAK8C,GAErBD,EAAc7C,KAAK,CAAC8C,GAE5B,MACID,EAAc7C,KAAK8C,GAI3B,OAAOD,CAAa,EAiGEK,CAAiBnH,EAAWY,GAElD,OAA6B,IAAzBkG,EAAcxD,OAAqB,KAEhCwD,EAAcM,KAAI,CAACxD,EAAMyD,IACxBJ,MAAMC,QAAQtD,GA1CI,EAC1B5D,EACAsH,KAEA,MAAMC,EAAgBvH,EAAUoH,KAAKxD,IACjC,MAAM4D,EAAerE,OAAAwB,OAAA,CAAA,EAAQf,GAM7B,YAL8CS,IAA1CvC,EAAqBJ,QAAQkC,EAAKE,MAClC0D,EAAgBhD,YACZ1C,EAAqBJ,QAAQkC,EAAKE,KAItC2D,EAACC,EAAY,CAETX,SAAUS,EACV5F,aAAcA,EACd1B,yBAA0BA,EAC1BC,oBAAqBA,EACrBC,iBAAkBA,EAClBuH,OAAQpD,EAAeX,GACvBgE,SAAU9C,EAAalB,GACvBiE,OAAQjD,EAAehB,IARlBA,EAAKE,GASZ,IAIV,OACI2D,EAAA,KAAA,CAEIK,UAAWC,EAA6BC,SAExCP,EAAA,KAAA,CAAAO,SAAKT,KAHA,YAAYD,IAIhB,EAWMW,CAAsBrE,EAAMyD,GAG/BI,EAACS,EAAY,CAETnB,SAAUnD,EACVuE,SAAUxC,GAAc/B,GACxBhC,aAAcA,EACdvB,SAAU8F,KACV7F,SAAUA,EACVC,SAAUA,EACVH,iBAAkBA,EAClBgI,SAAUpD,GAAapB,GACvByE,YAAatD,GAAmBnB,IAT3BA,EAAKE,KAaxB,EAiBAwE,GAAyB,IAEvBC,EAAAC,EAAA,CAAAR,SAAA,CAPJP,EAACgB,EAAc,CAAA,YAAW,uBAAqB,OAAMT,SAChD9G,IAQGuG,QACIiB,UAAU,EACVZ,UAAWa,EACPC,EACArI,GAAYsI,GAEhBlI,IAAKc,EAAU,aACH6E,KAAqB0B,SAEhCnB,UAMjB,OAAIvG,GAAYC,IAAa4F,KAClBmC,KAGHb,EAACqB,EAAU,CACP/G,QAASA,EACTgH,UAAW9D,GACX+D,YAAatD,GAAesC,SAE5BP,EAACwB,EAAe,CACZC,MAAOlJ,EACPmJ,SAAUC,EAA2BpB,SAEpCM,QAKrB"}
|
|
1
|
+
{"version":3,"file":"file-list.js","sources":["../../../src/file-upload/file-list/file-list.tsx"],"sourcesContent":["import type { DragEndEvent, DragStartEvent } from \"@dnd-kit/core\";\nimport {\n DndContext,\n KeyboardSensor,\n TouchSensor,\n useSensor,\n useSensors,\n} from \"@dnd-kit/core\";\nimport {\n arrayMove,\n SortableContext,\n sortableKeyboardCoordinates,\n verticalListSortingStrategy,\n} from \"@dnd-kit/sortable\";\nimport clsx from \"clsx\";\nimport type React from \"react\";\nimport {\n forwardRef,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\nimport { useResizeDetector } from \"react-resize-detector\";\n\nimport type { FormLabelProps } from \"../../form/form-label/types\";\nimport { VisuallyHidden } from \"../../shared/accessibility\";\nimport { FileUploadContext } from \"../context\";\nimport { MouseSensor } from \"../custom-sensors\";\nimport { FileListItem } from \"../file-list-item\";\nimport type { FileItemMode } from \"../file-list-item/types\";\nimport { FileUploadHelper } from \"../helper\";\nimport type { FileItemProps } from \"../types\";\nimport * as styles from \"./file-list.styles\";\n\n// =============================================================================\n// INTERFACES\n// =============================================================================\ntype FileEditedDescriptions = Record<string, string>;\n\ninterface Props {\n fileItems: FileItemProps[] | undefined;\n editableFileItems: boolean;\n fileDescriptionMaxLength?: number | undefined;\n descriptionRequired?: boolean | undefined;\n descriptionLabel?: FormLabelProps | undefined;\n sortable?: boolean | undefined;\n disabled?: boolean | undefined;\n readOnly?: boolean | undefined;\n onItemUpdate: (item: FileItemProps) => void;\n onItemDelete: (item: FileItemProps) => void;\n onSort?: ((reorderedFileItems: FileItemProps[]) => void) | undefined;\n}\n\ntype AnnouncementProgress = Record<\n string,\n {\n progress: number;\n timestamp: number;\n status: \"in-progress\" | \"complete\" | \"error\";\n }\n>;\n\nexport interface FileListRef {\n focus: () => void;\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\nfunction Component(\n {\n fileItems = [],\n editableFileItems,\n fileDescriptionMaxLength,\n descriptionRequired,\n descriptionLabel,\n sortable,\n disabled,\n readOnly,\n onItemUpdate,\n onItemDelete,\n onSort,\n }: Props,\n ref: React.Ref<FileListRef>\n) {\n // =========================================================================\n // CONST, STATE, REFS\n // =========================================================================\n const { setActiveId } = useContext(FileUploadContext);\n const [editingCount, setEditingCount] = useState(0);\n\n // Progress announcement state (for aria-live) - announces start and completion only\n const [progressAnnouncement, setProgressAnnouncement] = useState(\"\");\n const lastAnnouncedRef = useRef<AnnouncementProgress>({});\n\n useImperativeHandle(ref, () => ({\n focus: () => {\n wrapperRef.current?.focus();\n },\n }));\n const { width: wrapperWidth = 0, ref: wrapperRef } = useResizeDetector();\n\n // Keep track of edited description without re-rendering\n const descriptionsValueRef = useRef<FileEditedDescriptions>({});\n\n /**\n * As the default drag sensors interfere with click events\n * on the file items, we'll need to configure the sensor to\n * only activate the drag if the mouse moves a certain distance\n */\n const sensors = useSensors(\n useSensor(MouseSensor, {\n activationConstraint: {\n distance: 8, // mouse drag of 8px then activate the drag event\n },\n }),\n useSensor(TouchSensor, {\n activationConstraint: {\n delay: 150,\n tolerance: 5,\n },\n }),\n useSensor(KeyboardSensor, {\n coordinateGetter: sortableKeyboardCoordinates,\n })\n );\n\n // =========================================================================\n // REF METHODS\n // =========================================================================\n const removeDescription = (itemId: string) => {\n delete descriptionsValueRef.current[itemId];\n };\n\n // =========================================================================\n // EFFECTS\n // =========================================================================\n // Progress announcements only at start and completion\n useEffect(() => {\n if (!fileItems || fileItems.length === 0) {\n setProgressAnnouncement(\"\");\n return;\n }\n const now = Date.now();\n const messages: string[] = [];\n\n for (const item of fileItems) {\n const fileStatus = lastAnnouncedRef.current[item.id];\n\n if (item.errorMessage) {\n if (fileStatus?.status !== \"error\") {\n messages.push(\n typeof item.errorMessage === \"string\"\n ? `Error uploading ${item.name}: ${item.errorMessage}`\n : `Error uploading ${item.name}`\n );\n lastAnnouncedRef.current[item.id] = {\n progress: item.progress ?? 1,\n timestamp: now,\n status: \"error\",\n };\n }\n continue;\n }\n\n if (item.progress === undefined) {\n continue;\n }\n\n if (item.progress >= 1) {\n if (fileStatus?.status !== \"complete\") {\n messages.push(`${item.name} upload is complete`);\n lastAnnouncedRef.current[item.id] = {\n progress: 1,\n timestamp: now,\n status: \"complete\",\n };\n }\n continue;\n }\n\n // In progress - only announce start, not progress updates\n if (typeof item.progress === \"number\" && item.progress < 1) {\n // Only announce when starting upload (no previous status or status was not in-progress)\n const shouldAnnounce =\n !fileStatus || fileStatus.status !== \"in-progress\";\n\n if (shouldAnnounce) {\n messages.push(`Starting upload of ${item.name}`);\n lastAnnouncedRef.current[item.id] = {\n progress: item.progress,\n timestamp: now,\n status: \"in-progress\",\n };\n }\n }\n }\n\n if (messages.length > 0) {\n // Update aria-live region once with aggregated message\n setProgressAnnouncement(messages.join(\", \"));\n }\n }, [fileItems]);\n\n // =========================================================================\n // EVENT HANDLERS\n // =========================================================================\n const handleSaveEdit = (item: FileItemProps) => (description: string) => {\n removeDescription(item.id);\n const updatedItem = { ...item, description };\n onItemUpdate(updatedItem);\n };\n\n const handleBlurEdit = (item: FileItemProps) => (value: string) => {\n descriptionsValueRef.current[item.id] = value;\n };\n\n const handleCancel = (item: FileItemProps) => () => {\n removeDescription(item.id);\n if (!item.description || item.description.length === 0) {\n onItemDelete(item);\n }\n };\n\n const handleModeChange = (mode: FileItemMode) => {\n setEditingCount((prev) =>\n mode === \"edit\" ? prev + 1 : Math.max(0, prev - 1)\n );\n };\n\n const handleDelete = (item: FileItemProps) => () => {\n onItemDelete(item);\n if (wrapperRef.current) {\n wrapperRef.current.focus();\n }\n };\n\n const handleDragEnd = (event: DragEndEvent) => {\n if (onSort && fileItems) {\n const { active, over } = event;\n\n if (over && active.id !== over.id) {\n const oldIndex = fileItems.findIndex(\n (item) => item.id === active.id\n );\n const newIndex = fileItems.findIndex(\n (item) => item.id === over.id\n );\n\n const updatedFileItems = arrayMove(\n fileItems,\n oldIndex,\n newIndex\n );\n\n onSort(updatedFileItems);\n }\n }\n\n setActiveId(undefined);\n };\n\n const handleDragStart = (event: DragStartEvent) => {\n const { active } = event;\n setActiveId(active.id as string);\n };\n\n // =========================================================================\n // HELPER FUNCTIONS\n // =========================================================================\n const checkEditable = (item: FileItemProps) => {\n return (\n editableFileItems &&\n FileUploadHelper.isSupportedImageType(item.type)\n );\n };\n\n const areAllItemsInDisplayViews = () => {\n if (editingCount > 0) return false;\n return !fileItems.some((item) => !!item.errorMessage);\n };\n\n const shouldEnableSort = () => {\n return (\n fileItems &&\n fileItems.length > 1 &&\n sortable &&\n !readOnly &&\n areAllItemsInDisplayViews()\n );\n };\n\n /**\n * If there are no files, return \"No files uploaded\".\n * If readOnly is true, return \"Read-only file list\".\n * If there are files, return a summary of the file statuses.\n * For example, \"File list. 2 completed files, 1 file in progress, 1 file with error\".\n *\n * @returns aria label\n */\n const getWrapperAriaLabel = () => {\n if (fileItems.length === 0) {\n return \"No files uploaded\";\n }\n const completedCount = fileItems.filter(\n (item) =>\n !item?.errorMessage &&\n (item.progress === 1 || item.progress === undefined)\n ).length;\n const inProgressCount = fileItems.filter(\n (item) => typeof item.progress === \"number\" && item.progress < 1\n ).length;\n const errorCount = fileItems.filter((item) => item.errorMessage).length;\n if (completedCount === 0 && inProgressCount === 0 && errorCount === 0) {\n return `File list`;\n }\n const completedText =\n completedCount > 0 ? `${completedCount} completed files` : \"\";\n const inProgressText =\n inProgressCount > 0 ? `${inProgressCount} file in progress` : \"\";\n const errorText =\n errorCount > 0\n ? `${errorCount} ${\n errorCount > 1 ? \"files\" : \"file\"\n } with error`\n : \"\";\n const parts = [completedText, inProgressText, errorText].filter(\n Boolean\n );\n const prefix = readOnly ? \"Read-only file list\" : \"File list\";\n return `${prefix}. ${parts.join(\", \")}`;\n };\n\n // =========================================================================\n // RENDER FUNCTIONS\n // =========================================================================\n const renderItems = () => {\n if (fileItems.length === 0) return null;\n\n return fileItems.map((item) => {\n const updatedFileItem = { ...item };\n if (descriptionsValueRef.current[item.id] !== undefined) {\n updatedFileItem.description =\n descriptionsValueRef.current[item.id];\n }\n\n return (\n <FileListItem\n key={item.id}\n fileItem={updatedFileItem}\n editable={checkEditable(item)}\n wrapperWidth={wrapperWidth}\n sortable={shouldEnableSort()}\n disabled={disabled}\n readOnly={readOnly}\n descriptionLabel={descriptionLabel}\n fileDescriptionMaxLength={fileDescriptionMaxLength}\n descriptionRequired={descriptionRequired}\n onDelete={handleDelete(item)}\n onSave={handleSaveEdit(item)}\n onCancel={handleCancel(item)}\n onBlur={handleBlurEdit(item)}\n onModeChange={handleModeChange}\n />\n );\n });\n };\n\n /**\n * Renders the progress status of each file upload.\n * In progress: 25% complete for uploading filename.pdf\n * Complete: filename.pdf upload is complete\n * Error: Error uploading filename.pdf: error message\n *\n * @returns A visually hidden element announcing the progress status.\n */\n const renderProgressStatus = () => (\n <VisuallyHidden aria-live=\"polite\" aria-atomic=\"true\">\n {progressAnnouncement}\n </VisuallyHidden>\n );\n\n const renderItemsWithWrapper = () => {\n return (\n <>\n {renderProgressStatus()}\n <ul\n tabIndex={-1}\n className={clsx(\n styles.listWrapper,\n readOnly && styles.listWrapperReadOnly\n )}\n ref={wrapperRef}\n aria-label={getWrapperAriaLabel()}\n >\n {renderItems()}\n </ul>\n </>\n );\n };\n\n return (\n <DndContext\n sensors={sensors}\n onDragEnd={handleDragEnd}\n onDragStart={handleDragStart}\n >\n <SortableContext\n items={fileItems}\n strategy={verticalListSortingStrategy}\n >\n {renderItemsWithWrapper()}\n </SortableContext>\n </DndContext>\n );\n}\n\nexport const FileList = forwardRef<FileListRef, Props>(Component);\n"],"names":["FileList","forwardRef","fileItems","editableFileItems","fileDescriptionMaxLength","descriptionRequired","descriptionLabel","sortable","disabled","readOnly","onItemUpdate","onItemDelete","onSort","ref","setActiveId","useContext","FileUploadContext","editingCount","setEditingCount","useState","progressAnnouncement","setProgressAnnouncement","lastAnnouncedRef","useRef","useImperativeHandle","focus","_a","wrapperRef","current","width","wrapperWidth","useResizeDetector","descriptionsValueRef","sensors","useSensors","useSensor","MouseSensor","activationConstraint","distance","TouchSensor","delay","tolerance","KeyboardSensor","coordinateGetter","sortableKeyboardCoordinates","removeDescription","itemId","useEffect","length","now","Date","messages","item","fileStatus","id","errorMessage","status","push","name","progress","timestamp","undefined","join","handleSaveEdit","description","updatedItem","Object","assign","handleBlurEdit","value","handleCancel","handleModeChange","mode","prev","Math","max","handleDelete","checkEditable","FileUploadHelper","isSupportedImageType","type","shouldEnableSort","some","getWrapperAriaLabel","completedCount","filter","inProgressCount","errorCount","parts","Boolean","_jsx","DndContext","onDragEnd","event","active","over","oldIndex","findIndex","newIndex","updatedFileItems","arrayMove","onDragStart","children","SortableContext","items","strategy","verticalListSortingStrategy","_jsxs","_Fragment","VisuallyHidden","tabIndex","className","clsx","styles.listWrapper","styles.listWrapperReadOnly","map","updatedFileItem","FileListItem","fileItem","editable","onDelete","onSave","onCancel","onBlur","onModeChange"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import"./file-list.
|
|
1
|
+
import"./file-list.styles_1xbypnh.css";const l="l14goqmf",s="l1spcgo1";export{l as listWrapper,s as listWrapperReadOnly};
|
|
2
2
|
//# sourceMappingURL=file-list.styles.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-list.styles.js","sources":["../../../src/file-upload/file-list/file-list.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport { Spacing } from \"../../theme\";\n\nexport const listWrapper = css`\n list-style-type: none;\n margin-bottom: ${Spacing[\"spacing-32\"]};\n`;\n\nexport const listWrapperReadOnly = css`\n margin-bottom: 0;\n`;\n
|
|
1
|
+
{"version":3,"file":"file-list.styles.js","sources":["../../../src/file-upload/file-list/file-list.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport { Spacing } from \"../../theme\";\n\nexport const listWrapper = css`\n list-style-type: none;\n margin-bottom: ${Spacing[\"spacing-32\"]};\n`;\n\nexport const listWrapperReadOnly = css`\n margin-bottom: 0;\n`;\n"],"names":["listWrapper","listWrapperReadOnly"],"mappings":"4EAIO,MAAMA,EAAW,WAKXC,EAAmB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
interface BaseProps {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
interface ErrorActionProps extends BaseProps {
|
|
6
|
+
mode: "error";
|
|
7
|
+
errorMessage?: string | React.ReactNode | undefined;
|
|
8
|
+
onDelete: () => void;
|
|
9
|
+
}
|
|
10
|
+
interface DisplayActionProps extends BaseProps {
|
|
11
|
+
mode: "display";
|
|
12
|
+
inline?: boolean | undefined;
|
|
13
|
+
editable?: boolean | undefined;
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
progress: number;
|
|
16
|
+
disabled: boolean;
|
|
17
|
+
onDelete: () => void;
|
|
18
|
+
onEdit?: (() => void) | undefined;
|
|
19
|
+
onKeyDown?: ((event: React.KeyboardEvent<HTMLButtonElement>) => void) | undefined;
|
|
20
|
+
}
|
|
21
|
+
interface EditActionProps extends BaseProps {
|
|
22
|
+
mode: "edit";
|
|
23
|
+
hasThumbnail: boolean;
|
|
24
|
+
disableSave: boolean;
|
|
25
|
+
onSave: () => void;
|
|
26
|
+
onCancel: () => void;
|
|
27
|
+
}
|
|
28
|
+
type FileItemActionsProps = ErrorActionProps | DisplayActionProps | EditActionProps;
|
|
29
|
+
export declare const FileItemActions: (props: FileItemActionsProps) => import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{jsx as e,jsxs as t,Fragment as a}from"react/jsx-runtime";import{BinIcon as i}from"@lifesg/react-icons/bin";import{PencilIcon as d}from"@lifesg/react-icons/pencil";import l from"clsx";import{Button as n}from"../../button/button.js";import{ProgressBar as o}from"../../shared/progress-bar/progress-bar.js";import{actionContainer as s,iconButton as r,actionButton as c,actionButtonsSection as b,actionButtonsSectionWithThumbnail as m,inlineActionContainer as y}from"./file-item-actions.styles.js";const p=p=>{const{mode:u,id:h,name:g}=p;if("error"===u)return e("div",{className:s,children:e(n,{"data-testid":`${h}-error-delete-button`,"data-no-dnd":"true",type:"button",styleType:"light",sizeType:"small","aria-label":`delete ${g}, error: ${p.errorMessage}`,onClick:p.onDelete,className:r,icon:e(i,{"aria-hidden":!0})})});if("edit"===u)return t("div",{className:l(b,p.hasThumbnail&&m),children:[e(n,{"data-testid":`${h}-save-button`,type:"button",sizeType:"small",disabled:p.disableSave,onClick:p.onSave,className:c,children:"Save"}),e(n,{type:"button",sizeType:"small",styleType:"secondary","data-testid":`${h}-cancel-button`,onClick:p.onCancel,className:c,children:"Cancel"})]});if(p.inline)return t("div",{className:y,children:[e(n,{"data-testid":`${h}-edit-button`,"data-no-dnd":"true",type:"button",styleType:"light",sizeType:"small","aria-label":`edit ${g}`,disabled:p.disabled,onClick:p.onEdit,onKeyDown:p.onKeyDown,icon:e(d,{"aria-hidden":!0}),className:r},"edit"),e(n,{"data-testid":`${h}-delete-button`,"data-no-dnd":"true",type:"button",styleType:"light",sizeType:"small","aria-label":`delete ${g}`,disabled:p.disabled,onClick:p.onDelete,onKeyDown:p.onKeyDown,icon:e(i,{"aria-hidden":!0}),className:r},"delete")]});let T;return T=p.isLoading?e(o,{progress:p.progress,"data-testid":`${h}-progress-bar`}):t(a,{children:[p.editable&&e(n,{"data-testid":`${h}-edit-button`,"data-no-dnd":"true",type:"button",styleType:"light",sizeType:"small","aria-label":`edit ${g}`,disabled:p.disabled,onClick:p.onEdit,onKeyDown:p.onKeyDown,icon:e(d,{"aria-hidden":!0}),className:r},"edit"),e(n,{"data-testid":`${h}-delete-button`,"data-no-dnd":"true",type:"button",styleType:"light",sizeType:"small","aria-label":`delete ${g}`,disabled:p.disabled,onClick:p.onDelete,onKeyDown:p.onKeyDown,icon:e(i,{"aria-hidden":!0}),className:r},"delete")]}),e("div",{className:s,"data-mobile-layout":p.isLoading?"loading":p.editable?"editable":void 0,children:T})};export{p as FileItemActions};
|
|
2
|
+
//# sourceMappingURL=file-item-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-item-actions.js","sources":["../../../src/file-upload/file-list-item/file-item-actions.tsx"],"sourcesContent":["import { BinIcon } from \"@lifesg/react-icons/bin\";\nimport { PencilIcon } from \"@lifesg/react-icons/pencil\";\nimport clsx from \"clsx\";\n\nimport { Button } from \"../../button\";\nimport { ProgressBar } from \"../../shared/progress-bar\";\nimport * as styles from \"./file-item-actions.styles\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\ninterface BaseProps {\n id: string;\n name: string;\n}\n\ninterface ErrorActionProps extends BaseProps {\n mode: \"error\";\n errorMessage?: string | React.ReactNode | undefined;\n onDelete: () => void;\n}\n\ninterface DisplayActionProps extends BaseProps {\n mode: \"display\";\n inline?: boolean | undefined;\n editable?: boolean | undefined;\n isLoading: boolean;\n progress: number;\n disabled: boolean;\n onDelete: () => void;\n onEdit?: (() => void) | undefined;\n onKeyDown?:\n | ((event: React.KeyboardEvent<HTMLButtonElement>) => void)\n | undefined;\n}\n\ninterface EditActionProps extends BaseProps {\n mode: \"edit\";\n hasThumbnail: boolean;\n disableSave: boolean;\n onSave: () => void;\n onCancel: () => void;\n}\n\ntype FileItemActionsProps =\n | ErrorActionProps\n | DisplayActionProps\n | EditActionProps;\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\nexport const FileItemActions = (props: FileItemActionsProps) => {\n const { mode, id, name } = props;\n\n if (mode === \"error\") {\n return (\n <div className={styles.actionContainer}>\n <Button\n data-testid={`${id}-error-delete-button`}\n data-no-dnd=\"true\"\n type=\"button\"\n styleType=\"light\"\n sizeType=\"small\"\n aria-label={`delete ${name}, error: ${props.errorMessage}`}\n onClick={props.onDelete}\n className={styles.iconButton}\n icon={<BinIcon aria-hidden />}\n />\n </div>\n );\n }\n\n if (mode === \"edit\") {\n return (\n <div\n className={clsx(\n styles.actionButtonsSection,\n props.hasThumbnail &&\n styles.actionButtonsSectionWithThumbnail\n )}\n >\n <Button\n data-testid={`${id}-save-button`}\n type=\"button\"\n sizeType=\"small\"\n disabled={props.disableSave}\n onClick={props.onSave}\n className={styles.actionButton}\n >\n Save\n </Button>\n <Button\n type=\"button\"\n sizeType=\"small\"\n styleType=\"secondary\"\n data-testid={`${id}-cancel-button`}\n onClick={props.onCancel}\n className={styles.actionButton}\n >\n Cancel\n </Button>\n </div>\n );\n }\n\n // Display mode\n if (props.inline) {\n return (\n <div className={styles.inlineActionContainer}>\n <Button\n key=\"edit\"\n data-testid={`${id}-edit-button`}\n data-no-dnd=\"true\"\n type=\"button\"\n styleType=\"light\"\n sizeType=\"small\"\n aria-label={`edit ${name}`}\n disabled={props.disabled}\n onClick={props.onEdit}\n onKeyDown={props.onKeyDown}\n icon={<PencilIcon aria-hidden />}\n className={styles.iconButton}\n />\n <Button\n key=\"delete\"\n data-testid={`${id}-delete-button`}\n data-no-dnd=\"true\"\n type=\"button\"\n styleType=\"light\"\n sizeType=\"small\"\n aria-label={`delete ${name}`}\n disabled={props.disabled}\n onClick={props.onDelete}\n onKeyDown={props.onKeyDown}\n icon={<BinIcon aria-hidden />}\n className={styles.iconButton}\n />\n </div>\n );\n }\n\n const getActionContainerLayout = () => {\n if (props.isLoading) return \"loading\";\n if (props.editable) return \"editable\";\n return undefined;\n };\n\n let content: JSX.Element;\n\n if (props.isLoading) {\n content = (\n <ProgressBar\n progress={props.progress}\n data-testid={`${id}-progress-bar`}\n />\n );\n } else {\n content = (\n <>\n {props.editable && (\n <Button\n key=\"edit\"\n data-testid={`${id}-edit-button`}\n data-no-dnd=\"true\"\n type=\"button\"\n styleType=\"light\"\n sizeType=\"small\"\n aria-label={`edit ${name}`}\n disabled={props.disabled}\n onClick={props.onEdit}\n onKeyDown={props.onKeyDown}\n icon={<PencilIcon aria-hidden />}\n className={styles.iconButton}\n />\n )}\n <Button\n key=\"delete\"\n data-testid={`${id}-delete-button`}\n data-no-dnd=\"true\"\n type=\"button\"\n styleType=\"light\"\n sizeType=\"small\"\n aria-label={`delete ${name}`}\n disabled={props.disabled}\n onClick={props.onDelete}\n onKeyDown={props.onKeyDown}\n icon={<BinIcon aria-hidden />}\n className={styles.iconButton}\n />\n </>\n );\n }\n\n return (\n <div\n className={styles.actionContainer}\n data-mobile-layout={getActionContainerLayout()}\n >\n {content}\n </div>\n );\n};\n"],"names":["FileItemActions","props","mode","id","name","_jsx","className","styles.actionContainer","children","Button","type","styleType","sizeType","errorMessage","onClick","onDelete","styles.iconButton","icon","BinIcon","_jsxs","clsx","styles.actionButtonsSection","hasThumbnail","styles.actionButtonsSectionWithThumbnail","disabled","disableSave","onSave","styles.actionButton","onCancel","inline","styles.inlineActionContainer","onEdit","onKeyDown","PencilIcon","content","isLoading","ProgressBar","progress","_Fragment","editable"],"mappings":"ofAoDO,MAAMA,EAAmBC,IAC5B,MAAMC,KAAEA,EAAIC,GAAEA,EAAEC,KAAEA,GAASH,EAE3B,GAAa,UAATC,EACA,OACIG,EAAA,MAAA,CAAKC,UAAWC,EAAsBC,SAClCH,EAACI,EAAM,CAAA,cACU,GAAGN,wBAAwB,cAC5B,OACZO,KAAK,SACLC,UAAU,QACVC,SAAS,qBACG,UAAUR,aAAgBH,EAAMY,eAC5CC,QAASb,EAAMc,SACfT,UAAWU,EACXC,KAAMZ,EAACa,EAAO,CAAA,eAAA,QAM9B,GAAa,SAAThB,EACA,OACIiB,EAAA,MAAA,CACIb,UAAWc,EACPC,EACApB,EAAMqB,cACFC,GACPf,SAAA,CAEDH,EAACI,EAAM,CAAA,cACU,GAAGN,gBAChBO,KAAK,SACLE,SAAS,QACTY,SAAUvB,EAAMwB,YAChBX,QAASb,EAAMyB,OACfpB,UAAWqB,EAAmBnB,SAAA,SAIlCH,EAACI,EAAM,CACHC,KAAK,SACLE,SAAS,QACTD,UAAU,YAAW,cACR,GAAGR,kBAChBW,QAASb,EAAM2B,SACftB,UAAWqB,EAAmBnB,SAAA,cAS9C,GAAIP,EAAM4B,OACN,OACIV,EAAA,MAAA,CAAKb,UAAWwB,EAA4BtB,SAAA,CACxCH,EAACI,EAAM,CAAA,cAEU,GAAGN,gBAAgB,cACpB,OACZO,KAAK,SACLC,UAAU,QACVC,SAAS,QAAO,aACJ,QAAQR,IACpBoB,SAAUvB,EAAMuB,SAChBV,QAASb,EAAM8B,OACfC,UAAW/B,EAAM+B,UACjBf,KAAMZ,EAAC4B,EAAU,CAAA,eAAA,IACjB3B,UAAWU,GAXP,QAaRX,EAACI,EAAM,CAAA,cAEU,GAAGN,kBAAkB,cACtB,OACZO,KAAK,SACLC,UAAU,QACVC,SAAS,QAAO,aACJ,UAAUR,IACtBoB,SAAUvB,EAAMuB,SAChBV,QAASb,EAAMc,SACfiB,UAAW/B,EAAM+B,UACjBf,KAAMZ,EAACa,EAAO,CAAA,eAAA,IACdZ,UAAWU,GAXP,aAuBpB,IAAIkB,EA8CJ,OA3CIA,EADAjC,EAAMkC,UAEF9B,EAAC+B,EAAW,CACRC,SAAUpC,EAAMoC,uBACH,GAAGlC,mBAKpBgB,EAAAmB,EAAA,CAAA9B,SAAA,CACKP,EAAMsC,UACHlC,EAACI,EAAM,CAAA,cAEU,GAAGN,gBAAgB,cACpB,OACZO,KAAK,SACLC,UAAU,QACVC,SAAS,QAAO,aACJ,QAAQR,IACpBoB,SAAUvB,EAAMuB,SAChBV,QAASb,EAAM8B,OACfC,UAAW/B,EAAM+B,UACjBf,KAAMZ,EAAC4B,EAAU,CAAA,eAAA,IACjB3B,UAAWU,GAXP,QAcZX,EAACI,EAAM,CAAA,cAEU,GAAGN,kBAAkB,cACtB,OACZO,KAAK,SACLC,UAAU,QACVC,SAAS,QAAO,aACJ,UAAUR,IACtBoB,SAAUvB,EAAMuB,SAChBV,QAASb,EAAMc,SACfiB,UAAW/B,EAAM+B,UACjBf,KAAMZ,EAACa,EAAO,CAAA,eAAA,IACdZ,UAAWU,GAXP,aAkBhBX,EAAA,MAAA,CACIC,UAAWC,EAAsB,qBArDjCN,EAAMkC,UAAkB,UACxBlC,EAAMsC,SAAiB,gBAA3B,EAqDkD/B,SAE7C0B,GACC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const actionContainer: import("@linaria/core").LinariaClassName;
|
|
2
|
+
export declare const inlineActionContainer: import("@linaria/core").LinariaClassName;
|
|
3
|
+
export declare const actionButtonsSection: import("@linaria/core").LinariaClassName;
|
|
4
|
+
export declare const actionButtonsSectionWithThumbnail: import("@linaria/core").LinariaClassName;
|
|
5
|
+
export declare const actionButton: import("@linaria/core").LinariaClassName;
|
|
6
|
+
export declare const iconButton: import("@linaria/core").LinariaClassName;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./file-item-actions.styles_1quulgy.css";const i="a1j9d2dw",t="i1c7erb1",e="ax57y32",l="a1r3bkt9",s="a4qbgp8",a="i1m4nw9n";export{s as actionButton,e as actionButtonsSection,l as actionButtonsSectionWithThumbnail,i as actionContainer,a as iconButton,t as inlineActionContainer};
|
|
2
|
+
//# sourceMappingURL=file-item-actions.styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-item-actions.styles.js","sources":["../../../src/file-upload/file-list-item/file-item-actions.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport { MediaQuery, Spacing } from \"../../theme\";\n\nexport const actionContainer = css`\n width: 6rem;\n margin-left: ${Spacing[\"spacing-32\"]};\n display: flex;\n justify-content: flex-end;\n align-items: center;\n\n ${MediaQuery.MaxWidth.md} {\n width: fit-content;\n }\n\n &[data-mobile-layout=\"loading\"] {\n ${MediaQuery.MaxWidth.md} {\n margin-left: 0;\n margin-top: ${Spacing[\"spacing-16\"]};\n width: 100%;\n }\n }\n\n &[data-mobile-layout=\"editable\"] {\n ${MediaQuery.MaxWidth.md} {\n margin-left: 0;\n margin-top: ${Spacing[\"spacing-16\"]};\n align-self: flex-end;\n }\n }\n`;\n\nexport const inlineActionContainer = css`\n width: 100%;\n margin-top: ${Spacing[\"spacing-16\"]};\n display: flex;\n justify-content: flex-end;\n align-items: center;\n`;\n\nexport const actionButtonsSection = css`\n display: flex;\n justify-content: flex-end;\n\n ${MediaQuery.MaxWidth.md} {\n flex-direction: column;\n }\n`;\n\nexport const actionButtonsSectionWithThumbnail = css`\n margin-left: calc(\n 96px + ${Spacing[\"spacing-32\"]}\n ); // thumbnail width + right margin\n\n ${MediaQuery.MaxWidth.md} {\n margin-left: 0;\n }\n`;\n\nexport const actionButton = css`\n width: 7.5rem;\n\n &:not(:last-of-type) {\n margin-right: ${Spacing[\"spacing-16\"]};\n }\n\n ${MediaQuery.MaxWidth.md} {\n width: 100%;\n\n &:not(:last-of-type) {\n margin-bottom: ${Spacing[\"spacing-16\"]};\n }\n }\n`;\n\nexport const iconButton = css`\n min-width: unset;\n\n &:not(:last-child) {\n margin-right: ${Spacing[\"spacing-8\"]};\n }\n`;\n"],"names":["actionContainer","inlineActionContainer","actionButtonsSection","actionButtonsSectionWithThumbnail","actionButton","iconButton"],"mappings":"yFAIO,MAAMA,EAAe,WA4BfC,EAAqB,WAQrBC,EAAoB,UASpBC,EAAiC,WAUjCC,EAAY,UAgBZC,EAAU"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
@layer fds{.a1j9d2dw{width:6rem;margin-left:var(--fds-spacing-32);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;-webkit-justify-content:flex-end;justify-content:flex-end;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}:where(body.fds-breakpoint-md-max) .a1j9d2dw{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;}:where(body.fds-breakpoint-md-max) .a1j9d2dw[data-mobile-layout="loading"]{margin-left:0;margin-top:var(--fds-spacing-16);width:100%;}:where(body.fds-breakpoint-md-max) .a1j9d2dw[data-mobile-layout="editable"]{margin-left:0;margin-top:var(--fds-spacing-16);-webkit-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end;}
|
|
2
|
+
.i1c7erb1{width:100%;margin-top:var(--fds-spacing-16);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;-webkit-justify-content:flex-end;justify-content:flex-end;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}
|
|
3
|
+
.ax57y32{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;-webkit-justify-content:flex-end;justify-content:flex-end;}:where(body.fds-breakpoint-md-max) .ax57y32{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;}
|
|
4
|
+
.a1r3bkt9{margin-left:calc(
|
|
5
|
+
96px + var(--fds-spacing-32)
|
|
6
|
+
);}:where(body.fds-breakpoint-md-max) .a1r3bkt9{margin-left:0;}
|
|
7
|
+
.a4qbgp8{width:7.5rem;}.a4qbgp8:not(:last-of-type){margin-right:var(--fds-spacing-16);}:where(body.fds-breakpoint-md-max) .a4qbgp8{width:100%;}:where(body.fds-breakpoint-md-max) .a4qbgp8:not(:last-of-type){margin-bottom:var(--fds-spacing-16);}
|
|
8
|
+
.i1m4nw9n{min-width:unset;}.i1m4nw9n:not(:last-child){margin-right:var(--fds-spacing-8);}}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FormLabelProps } from "../../form/form-label/types";
|
|
2
|
+
import type { FileItemMode } from "./types";
|
|
3
|
+
interface Props {
|
|
4
|
+
mode: Exclude<FileItemMode, "edit">;
|
|
5
|
+
formattedName: string | undefined;
|
|
6
|
+
description: string | undefined;
|
|
7
|
+
fileSize: string;
|
|
8
|
+
errorMessage: string | React.ReactNode | undefined;
|
|
9
|
+
isLoading: boolean;
|
|
10
|
+
descriptionLabel?: FormLabelProps | undefined;
|
|
11
|
+
detailSectionRef: React.Ref<HTMLDivElement>;
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
export declare const FileItemDetails: ({ mode, formattedName, description, fileSize, errorMessage, isLoading, descriptionLabel, detailSectionRef, children, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{jsxs as e,jsx as i,Fragment as r}from"react/jsx-runtime";import{ExclamationCircleFillIcon as l}from"@lifesg/react-icons/exclamation-circle-fill";import{Typography as d}from"../../typography/index.js";import{nameSection as o,descriptionFileSizeText as s,errorMessage as a,errorIcon as c,itemDescriptionLabel as n,itemDescriptionText as t}from"./file-item-details.styles.js";const m=({mode:m,formattedName:h,description:f,fileSize:p,errorMessage:y,isLoading:g,descriptionLabel:N,detailSectionRef:M,children:B})=>{var u;return e("div","error"===m?{ref:M,className:o,children:[i(d.BodyMD,{weight:f?"semibold":"regular",children:h}),i(d.BodyMD,{className:s,children:p}),y&&e(d.BodySM,{className:a,weight:"semibold",children:[i(l,{className:c,"aria-hidden":!0}),y]})]}:{ref:M,className:o,children:[i(d.BodyMD,{weight:f?"semibold":"regular",children:h}),f&&e(r,{children:[i(d.BodyMD,{className:n,children:null!==(u=null==N?void 0:N.children)&&void 0!==u?u:"Photo description"}),i(d.BodyMD,{className:t,children:f})]}),!g&&i(d.BodyMD,{className:s,children:p}),B]})};export{m as FileItemDetails};
|
|
2
|
+
//# sourceMappingURL=file-item-details.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-item-details.js","sources":["../../../src/file-upload/file-list-item/file-item-details.tsx"],"sourcesContent":["import { ExclamationCircleFillIcon } from \"@lifesg/react-icons/exclamation-circle-fill\";\n\nimport type { FormLabelProps } from \"../../form/form-label/types\";\nimport { Typography } from \"../../typography\";\nimport * as styles from \"./file-item-details.styles\";\nimport type { FileItemMode } from \"./types\";\n\ninterface Props {\n mode: Exclude<FileItemMode, \"edit\">;\n formattedName: string | undefined;\n description: string | undefined;\n fileSize: string;\n errorMessage: string | React.ReactNode | undefined;\n isLoading: boolean;\n descriptionLabel?: FormLabelProps | undefined;\n detailSectionRef: React.Ref<HTMLDivElement>;\n children?: React.ReactNode;\n}\n\nexport const FileItemDetails = ({\n mode,\n formattedName,\n description,\n fileSize,\n errorMessage,\n isLoading,\n descriptionLabel,\n detailSectionRef,\n children,\n}: Props) => {\n if (mode === \"error\") {\n return (\n <div ref={detailSectionRef} className={styles.nameSection}>\n <Typography.BodyMD\n weight={description ? \"semibold\" : \"regular\"}\n >\n {formattedName}\n </Typography.BodyMD>\n <Typography.BodyMD className={styles.descriptionFileSizeText}>\n {fileSize}\n </Typography.BodyMD>\n {errorMessage && (\n <Typography.BodySM\n className={styles.errorMessage}\n weight=\"semibold\"\n >\n <ExclamationCircleFillIcon\n className={styles.errorIcon}\n aria-hidden\n />\n {errorMessage}\n </Typography.BodySM>\n )}\n </div>\n );\n }\n\n return (\n <div ref={detailSectionRef} className={styles.nameSection}>\n <Typography.BodyMD weight={description ? \"semibold\" : \"regular\"}>\n {formattedName}\n </Typography.BodyMD>\n {description && (\n <>\n <Typography.BodyMD className={styles.itemDescriptionLabel}>\n {descriptionLabel?.children ?? \"Photo description\"}\n </Typography.BodyMD>\n <Typography.BodyMD className={styles.itemDescriptionText}>\n {description}\n </Typography.BodyMD>\n </>\n )}\n {!isLoading && (\n <Typography.BodyMD className={styles.descriptionFileSizeText}>\n {fileSize}\n </Typography.BodyMD>\n )}\n {children}\n </div>\n );\n};\n"],"names":["FileItemDetails","mode","formattedName","description","fileSize","errorMessage","isLoading","descriptionLabel","detailSectionRef","children","_jsxs","ref","className","styles.nameSection","_jsx","Typography","BodyMD","weight","styles.descriptionFileSizeText","BodySM","styles.errorMessage","ExclamationCircleFillIcon","styles.errorIcon","styles.itemDescriptionLabel","_a","styles.itemDescriptionText"],"mappings":"4XAmBO,MAAMA,EAAkB,EAC3BC,OACAC,gBACAC,cACAC,wBACAC,EACAC,YACAC,mBACAC,mBACAC,qBAEA,OAEQC,EAAA,MAFK,UAATT,EAEI,CAAKU,IAAKH,EAAkBI,UAAWC,EAAkBJ,SAAA,CACrDK,EAACC,EAAWC,OAAM,CACdC,OAAQd,EAAc,WAAa,UAASM,SAE3CP,IAELY,EAACC,EAAWC,QAAOJ,UAAWM,EAA8BT,SACvDL,IAEJC,GACGK,EAACK,EAAWI,OAAM,CACdP,UAAWQ,EACXH,OAAO,WAAUR,SAAA,CAEjBK,EAACO,EAAyB,CACtBT,UAAWU,qBAGdjB,QAQZM,IAAKH,EAAkBI,UAAWC,EAAkBJ,SAAA,CACrDK,EAACC,EAAWC,QAAOC,OAAQd,EAAc,WAAa,UAASM,SAC1DP,IAEJC,GACGO,eACII,EAACC,EAAWC,OAAM,CAACJ,UAAWW,EAA2Bd,SAC1B,QAA1Be,EAAAjB,eAAAA,EAAkBE,gBAAQ,IAAAe,EAAAA,EAAI,sBAEnCV,EAACC,EAAWC,QAAOJ,UAAWa,EAA0BhB,SACnDN,QAIXG,GACEQ,EAACC,EAAWC,OAAM,CAACJ,UAAWM,WACzBd,IAGRK,IACC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const nameSection: import("@linaria/core").LinariaClassName;
|
|
2
|
+
export declare const extendedNameSection: import("@linaria/core").LinariaClassName;
|
|
3
|
+
export declare const itemDescriptionLabel: import("@linaria/core").LinariaClassName;
|
|
4
|
+
export declare const itemDescriptionText: import("@linaria/core").LinariaClassName;
|
|
5
|
+
export declare const descriptionFileSizeText: import("@linaria/core").LinariaClassName;
|
|
6
|
+
export declare const errorIcon: import("@linaria/core").LinariaClassName;
|
|
7
|
+
export declare const errorMessage: import("@linaria/core").LinariaClassName;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./file-item-details.styles_1ce9akx.css";const e="naum77y",i="i11dywsu",s="i745c9",t="d1cbfa1y",l="e1dtq2ug",c="e1scdqz1";export{t as descriptionFileSizeText,l as errorIcon,c as errorMessage,i as itemDescriptionLabel,s as itemDescriptionText,e as nameSection};
|
|
2
|
+
//# sourceMappingURL=file-item-details.styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-item-details.styles.js","sources":["../../../src/file-upload/file-list-item/file-item-details.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport { Colour, Font, MediaQuery, Spacing } from \"../../theme\";\n\nexport const nameSection = css`\n display: flex;\n flex: 1;\n flex-direction: column;\n width: 100%;\n`;\n\nexport const extendedNameSection = css`\n display: flex;\n flex: 1;\n flex-direction: column;\n align-items: flex-start;\n\n ${MediaQuery.MaxWidth.md} {\n width: 100%;\n }\n`;\n\nexport const itemDescriptionLabel = css`\n ${Font[\"form-label\"]}\n font-weight: ${Font.Spec[\"weight-semibold\"]};\n color: ${Colour[\"text-subtle\"]};\n margin-top: ${Spacing[\"spacing-16\"]};\n`;\n\nexport const itemDescriptionText = css`\n margin-top: ${Spacing[\"spacing-4\"]};\n overflow-wrap: anywhere;\n`;\n\nexport const descriptionFileSizeText = css`\n color: ${Colour[\"text-subtler\"]};\n margin-top: ${Spacing[\"spacing-16\"]};\n`;\n\nexport const errorIcon = css`\n height: 1lh;\n width: 1em;\n flex-shrink: 0;\n color: ${Colour[\"icon-error-strong\"]};\n`;\n\nexport const errorMessage = css`\n color: ${Colour[\"text-error\"]};\n margin-top: ${Spacing[\"spacing-4\"]};\n display: flex;\n gap: ${Spacing[\"spacing-4\"]};\n`;\n"],"names":["nameSection","itemDescriptionLabel","itemDescriptionText","descriptionFileSizeText","errorIcon","errorMessage"],"mappings":"yFAIO,MAAMA,EAAW,UAkBXC,EAAoB,WAOpBC,EAAmB,SAKnBC,EAAuB,WAKvBC,EAAS,WAOTC,EAAY"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
@layer fds{.naum77y{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;width:100%;}
|
|
2
|
+
.e13zkqng{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;}:where(body.fds-breakpoint-md-max) .e13zkqng{width:100%;}
|
|
3
|
+
.i11dywsu{font-family:var(--fds-font-family-body);font-variant:var(--fds-font-variant);font-size:var(--fds-font-form-label-size);font-weight:var(--fds-font-weight-semibold);line-height:var(--fds-font-form-label-lh);letter-spacing:var(--fds-font-form-label-ls);font-weight:var(--fds-font-weight-semibold);color:var(--fds-colour-text-subtle);margin-top:var(--fds-spacing-16);}
|
|
4
|
+
.i745c9{margin-top:var(--fds-spacing-4);overflow-wrap:anywhere;}
|
|
5
|
+
.d1cbfa1y{color:var(--fds-colour-text-subtler);margin-top:var(--fds-spacing-16);}
|
|
6
|
+
.e1dtq2ug{height:1lh;width:1em;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;color:var(--fds-colour-icon-error-strong);}
|
|
7
|
+
.e1scdqz1{color:var(--fds-colour-text-error);margin-top:var(--fds-spacing-4);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:var(--fds-spacing-4);}}
|
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
import type { FormLabelProps } from "../../form/form-label/types";
|
|
2
1
|
import type { FileListItemProps } from "./types";
|
|
3
|
-
|
|
4
|
-
readOnly?: boolean | undefined;
|
|
5
|
-
descriptionLabel?: FormLabelProps | undefined;
|
|
6
|
-
}
|
|
7
|
-
export declare const FileListItem: import("react").MemoExoticComponent<({ fileItem, editable, sortable, wrapperWidth, disabled, readOnly, descriptionLabel, onDelete, onEditClick, }: Props) => import("react/jsx-runtime").JSX.Element>;
|
|
8
|
-
export {};
|
|
2
|
+
export declare const FileListItem: import("react").MemoExoticComponent<({ fileItem, wrapperWidth, editable, sortable, disabled, readOnly, descriptionLabel, fileDescriptionMaxLength, descriptionRequired, onDelete, onSave, onCancel, onBlur, onModeChange, }: FileListItemProps) => import("react/jsx-runtime").JSX.Element>;
|