@lifesg/react-design-system 4.0.0-alpha.11 → 4.0.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/cjs/file-download/file-download.js +1 -1
  2. package/cjs/file-download/file-download.js.map +1 -1
  3. package/cjs/file-download/file-list-card/file-list-card.js +1 -1
  4. package/cjs/file-download/file-list-card/file-list-card.js.map +1 -1
  5. package/cjs/file-download/file-list-card/file-list-card.styles.js +1 -1
  6. package/cjs/file-download/file-list-card/file-list-card.styles.js.map +1 -1
  7. package/cjs/file-download/file-list-card/file-list-card.styles_lzbqkk.css +18 -0
  8. package/cjs/file-upload/helper.js +1 -1
  9. package/cjs/file-upload/helper.js.map +1 -1
  10. package/cjs/footer/footer-disclaimer-links-data.js +1 -1
  11. package/cjs/footer/footer-disclaimer-links-data.js.map +1 -1
  12. package/cjs/footer/footer.styles.js +1 -1
  13. package/cjs/footer/footer.styles.js.map +1 -1
  14. package/cjs/footer/{footer.styles_1msorhc.css → footer.styles_14hnlcq.css} +4 -4
  15. package/cjs/fullscreen-image-carousel/fullscreen-image-carousel.js +1 -1
  16. package/cjs/fullscreen-image-carousel/fullscreen-image-carousel.js.map +1 -1
  17. package/cjs/index.js +1 -1
  18. package/cjs/masthead/sgds-day.css +0 -1
  19. package/cjs/masthead/sgds-night.css +0 -1
  20. package/cjs/navbar/navbar-logo-data.js +1 -1
  21. package/cjs/navbar/navbar-logo-data.js.map +1 -1
  22. package/cjs/theme/index.js +1 -1
  23. package/cjs/theme/types.js +1 -1
  24. package/cjs/theme/types.js.map +1 -1
  25. package/file-download/file-download.d.ts +1 -1
  26. package/file-download/file-download.js +1 -1
  27. package/file-download/file-download.js.map +1 -1
  28. package/file-download/file-list-card/file-list-card.d.ts +1 -1
  29. package/file-download/file-list-card/file-list-card.js +1 -1
  30. package/file-download/file-list-card/file-list-card.js.map +1 -1
  31. package/file-download/file-list-card/file-list-card.styles.d.ts +2 -0
  32. package/file-download/file-list-card/file-list-card.styles.js +1 -1
  33. package/file-download/file-list-card/file-list-card.styles.js.map +1 -1
  34. package/file-download/file-list-card/file-list-card.styles_lzbqkk.css +18 -0
  35. package/file-download/file-list-card/types.d.ts +1 -0
  36. package/file-download/types.d.ts +13 -0
  37. package/file-upload/helper.d.ts +30 -0
  38. package/file-upload/helper.js +1 -1
  39. package/file-upload/helper.js.map +1 -1
  40. package/footer/footer-disclaimer-links-data.js +1 -1
  41. package/footer/footer-disclaimer-links-data.js.map +1 -1
  42. package/footer/footer.styles.js +1 -1
  43. package/footer/footer.styles.js.map +1 -1
  44. package/footer/{footer.styles_1msorhc.css → footer.styles_14hnlcq.css} +4 -4
  45. package/fullscreen-image-carousel/fullscreen-image-carousel.d.ts +1 -1
  46. package/fullscreen-image-carousel/fullscreen-image-carousel.js +1 -1
  47. package/fullscreen-image-carousel/fullscreen-image-carousel.js.map +1 -1
  48. package/fullscreen-image-carousel/types.d.ts +37 -0
  49. package/index.js +1 -1
  50. package/masthead/sgds-day.css +0 -1
  51. package/masthead/sgds-night.css +0 -1
  52. package/navbar/navbar-logo-data.js +1 -1
  53. package/navbar/navbar-logo-data.js.map +1 -1
  54. package/package.json +1 -1
  55. package/theme/index.js +1 -1
  56. package/theme/styles/sportsg-orange.css +779 -0
  57. package/theme/theme-provider/preset.d.ts +2 -0
  58. package/theme/types.d.ts +9 -3
  59. package/theme/types.js +1 -1
  60. package/theme/types.js.map +1 -1
  61. package/cjs/file-download/file-list-card/file-list-card.styles_18j2gut.css +0 -16
  62. package/file-download/file-list-card/file-list-card.styles_18j2gut.css +0 -16
@@ -2,4 +2,5 @@ import type { FileItemDownloadProps } from "../types";
2
2
  export interface FileListItemProps {
3
3
  fileItem: FileItemDownloadProps;
4
4
  onDownload: (file: FileItemDownloadProps) => void | Promise<void>;
5
+ onClick?: ((file: FileItemDownloadProps) => void) | undefined;
5
6
  }
@@ -30,6 +30,12 @@ export interface FileItemDownloadProps {
30
30
  * @default true
31
31
  */
32
32
  ready?: boolean | undefined;
33
+ /**
34
+ * Name for the card, used when `FileDownload` is given an `onClick`.
35
+ *
36
+ * @default the file name
37
+ */
38
+ clickLabel?: string | undefined;
33
39
  }
34
40
  /** Visual style variant controlling whether the component renders with a dashed border. */
35
41
  export type FileDownloadStyle = "bordered" | "no-border";
@@ -68,4 +74,11 @@ export interface FileDownloadProps {
68
74
  * @returns A `Promise` that resolves when the download is complete, or `void` for synchronous handling.
69
75
  */
70
76
  onDownload: (file: FileItemDownloadProps) => void | Promise<void>;
77
+ /**
78
+ * Called when a file card itself is activated, as opposed to its download button.
79
+ * When omitted, activating the card triggers `onDownload` instead.
80
+ *
81
+ * @param file The file item whose card was activated.
82
+ */
83
+ onClick?: ((file: FileItemDownloadProps) => void) | undefined;
71
84
  }
@@ -1,6 +1,36 @@
1
+ /**
2
+ * How a file's thumbnail slot should be rendered.
3
+ *
4
+ * - `image` — a thumbnail was supplied; render it at `src`
5
+ * - `pdf-icon` — a PDF with no thumbnail; render the PDF badge borderless
6
+ * - `none` — nothing to show; render no thumbnail slot at all
7
+ */
8
+ export type FileThumbnailDisplay = {
9
+ type: "image";
10
+ src: string;
11
+ } | {
12
+ type: "pdf-icon";
13
+ } | {
14
+ type: "none";
15
+ };
1
16
  export declare class FileUploadHelper {
2
17
  static readonly PDF_MIME_TYPE = "application/pdf";
3
18
  static readonly PDF_ICON_URL = "https://assets.life.gov.sg/react-design-system/img/upload/pdf.svg";
4
19
  static formatFileSizeDisplay(size?: number): string;
5
20
  static isSupportedImageType(type: string): boolean;
21
+ /**
22
+ * Decides how a file's thumbnail slot should render, given its MIME type
23
+ * and any thumbnail the consumer supplied.
24
+ *
25
+ * Lives on `FileUploadHelper` so `FileUpload` can adopt it later. Today
26
+ * only `FileDownload` calls it: `FileUpload` still decides this inline in
27
+ * `file-list-item-thumbnail.tsx`, and the two do NOT yet agree — for a
28
+ * non-PDF with no thumbnail this returns `"none"` while `FileUpload`
29
+ * renders an `<img>` with an empty `src`. Do not read this as an invariant
30
+ * the two components already share.
31
+ *
32
+ * Callers render the result with their own markup, because their thumbnails
33
+ * change size at different breakpoints.
34
+ */
35
+ static resolveThumbnailDisplay(mimeType: string | undefined, thumbnailImageDataUrl: string | undefined): FileThumbnailDisplay;
6
36
  }
@@ -1,2 +1,2 @@
1
- class e{static formatFileSizeDisplay(e){if(!e||0===e)return"0 KB";const a=Math.floor(Math.log(e)/Math.log(1024));return`${Number(e/Math.pow(1024,a)).toFixed(0)} ${["B","KB","MB","GB","TB","PB","EB","ZB","YB"][a]}`}static isSupportedImageType(e){return["image/avif","image/gif","image/jpeg","image/png","image/svg+xml","image/webp"].includes(e)}}e.PDF_MIME_TYPE="application/pdf",e.PDF_ICON_URL="https://assets.life.gov.sg/react-design-system/img/upload/pdf.svg";export{e as FileUploadHelper};
1
+ class e{static formatFileSizeDisplay(e){if(!e||0===e)return"0 KB";const t=Math.floor(Math.log(e)/Math.log(1024));return`${Number(e/Math.pow(1024,t)).toFixed(0)} ${["B","KB","MB","GB","TB","PB","EB","ZB","YB"][t]}`}static isSupportedImageType(e){return["image/avif","image/gif","image/jpeg","image/png","image/svg+xml","image/webp"].includes(e)}static resolveThumbnailDisplay(t,i){return i?{type:"image",src:i}:t===e.PDF_MIME_TYPE?{type:"pdf-icon"}:{type:"none"}}}e.PDF_MIME_TYPE="application/pdf",e.PDF_ICON_URL="https://assets.life.gov.sg/react-design-system/img/upload/pdf.svg";export{e as FileUploadHelper};
2
2
  //# sourceMappingURL=helper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helper.js","sources":["../../src/file-upload/helper.ts"],"sourcesContent":["export class FileUploadHelper {\n public static readonly PDF_MIME_TYPE = \"application/pdf\";\n public static readonly PDF_ICON_URL =\n \"https://assets.life.gov.sg/react-design-system/img/upload/pdf.svg\";\n\n // Adapted from https://stackoverflow.com/questions/15900485/correct-way-to-convert-size-in-size-to-kb-mb-gb-in-javascript\n public static formatFileSizeDisplay(size?: number) {\n if (!size || size === 0) return \"0 KB\";\n\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"];\n\n const index: number = Math.floor(Math.log(size) / Math.log(1024));\n\n const value = Number(size / Math.pow(1024, index)).toFixed(0);\n const measurement = sizes[index];\n\n return `${value} ${measurement}`;\n }\n\n public static isSupportedImageType(type: string): boolean {\n /** Currently only images supported by html <img> */\n const acceptedImageTypes = [\n \"image/avif\",\n \"image/gif\",\n \"image/jpeg\",\n \"image/png\",\n \"image/svg+xml\",\n \"image/webp\",\n ];\n\n return acceptedImageTypes.includes(type);\n }\n}\n"],"names":["FileUploadHelper","formatFileSizeDisplay","size","index","Math","floor","log","Number","pow","toFixed","isSupportedImageType","type","includes","PDF_MIME_TYPE","PDF_ICON_URL"],"mappings":"MAAaA,EAMF,4BAAOC,CAAsBC,GAChC,IAAKA,GAAiB,IAATA,EAAY,MAAO,OAEhC,MAEMC,EAAgBC,KAAKC,MAAMD,KAAKE,IAAIJ,GAAQE,KAAKE,IAAI,OAK3D,MAAO,GAHOC,OAAOL,EAAOE,KAAKI,IAAI,KAAML,IAAQM,QAAQ,MAJ7C,CAAC,IAAK,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,MAKpCN,IAG9B,CAEO,2BAAOO,CAAqBC,GAW/B,MAT2B,CACvB,aACA,YACA,aACA,YACA,gBACA,cAGsBC,SAASD,EACvC,EA9BuBX,EAAAa,cAAgB,kBAChBb,EAAAc,aACnB"}
1
+ {"version":3,"file":"helper.js","sources":["../../src/file-upload/helper.ts"],"sourcesContent":["/**\n * How a file's thumbnail slot should be rendered.\n *\n * - `image` — a thumbnail was supplied; render it at `src`\n * - `pdf-icon` — a PDF with no thumbnail; render the PDF badge borderless\n * - `none` — nothing to show; render no thumbnail slot at all\n */\nexport type FileThumbnailDisplay =\n | { type: \"image\"; src: string }\n | { type: \"pdf-icon\" }\n | { type: \"none\" };\n\nexport class FileUploadHelper {\n public static readonly PDF_MIME_TYPE = \"application/pdf\";\n public static readonly PDF_ICON_URL =\n \"https://assets.life.gov.sg/react-design-system/img/upload/pdf.svg\";\n\n // Adapted from https://stackoverflow.com/questions/15900485/correct-way-to-convert-size-in-size-to-kb-mb-gb-in-javascript\n public static formatFileSizeDisplay(size?: number) {\n if (!size || size === 0) return \"0 KB\";\n\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"];\n\n const index: number = Math.floor(Math.log(size) / Math.log(1024));\n\n const value = Number(size / Math.pow(1024, index)).toFixed(0);\n const measurement = sizes[index];\n\n return `${value} ${measurement}`;\n }\n\n public static isSupportedImageType(type: string): boolean {\n /** Currently only images supported by html <img> */\n const acceptedImageTypes = [\n \"image/avif\",\n \"image/gif\",\n \"image/jpeg\",\n \"image/png\",\n \"image/svg+xml\",\n \"image/webp\",\n ];\n\n return acceptedImageTypes.includes(type);\n }\n\n /**\n * Decides how a file's thumbnail slot should render, given its MIME type\n * and any thumbnail the consumer supplied.\n *\n * Lives on `FileUploadHelper` so `FileUpload` can adopt it later. Today\n * only `FileDownload` calls it: `FileUpload` still decides this inline in\n * `file-list-item-thumbnail.tsx`, and the two do NOT yet agree — for a\n * non-PDF with no thumbnail this returns `\"none\"` while `FileUpload`\n * renders an `<img>` with an empty `src`. Do not read this as an invariant\n * the two components already share.\n *\n * Callers render the result with their own markup, because their thumbnails\n * change size at different breakpoints.\n */\n public static resolveThumbnailDisplay(\n mimeType: string | undefined,\n thumbnailImageDataUrl: string | undefined\n ): FileThumbnailDisplay {\n if (thumbnailImageDataUrl) {\n return { type: \"image\", src: thumbnailImageDataUrl };\n }\n\n if (mimeType === FileUploadHelper.PDF_MIME_TYPE) {\n return { type: \"pdf-icon\" };\n }\n\n return { type: \"none\" };\n }\n}\n"],"names":["FileUploadHelper","formatFileSizeDisplay","size","index","Math","floor","log","Number","pow","toFixed","isSupportedImageType","type","includes","resolveThumbnailDisplay","mimeType","thumbnailImageDataUrl","src","PDF_MIME_TYPE","PDF_ICON_URL"],"mappings":"MAYaA,EAMF,4BAAOC,CAAsBC,GAChC,IAAKA,GAAiB,IAATA,EAAY,MAAO,OAEhC,MAEMC,EAAgBC,KAAKC,MAAMD,KAAKE,IAAIJ,GAAQE,KAAKE,IAAI,OAK3D,MAAO,GAHOC,OAAOL,EAAOE,KAAKI,IAAI,KAAML,IAAQM,QAAQ,MAJ7C,CAAC,IAAK,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,MAKpCN,IAG9B,CAEO,2BAAOO,CAAqBC,GAW/B,MAT2B,CACvB,aACA,YACA,aACA,YACA,gBACA,cAGsBC,SAASD,EACvC,CAgBO,8BAAOE,CACVC,EACAC,GAEA,OAAIA,EACO,CAAEJ,KAAM,QAASK,IAAKD,GAG7BD,IAAad,EAAiBiB,cACvB,CAAEN,KAAM,YAGZ,CAAEA,KAAM,OACnB,EA3DuBX,EAAAiB,cAAgB,kBAChBjB,EAAAkB,aACnB"}
@@ -1,2 +1,2 @@
1
- const e={privacy:{href:"https://www.life.gov.sg/privacy-statement",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://www.life.gov.sg/terms-of-use",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},r={privacy:{href:"https://assets.life.gov.sg/bookingsg/BSG_Privacy_Policy.pdf",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://assets.life.gov.sg/bookingsg/BSG_Terms_of_Use.pdf",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},t={privacy:{href:"https://www.imda.gov.sg/privacy-statement",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://www.imda.gov.sg/terms-of-use",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://www.tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},o={privacy:{href:"https://mylegacy.life.gov.sg/privacy-statement",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://mylegacy.life.gov.sg/terms-of-use",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},n={privacy:{href:"https://www.police.gov.sg/Privacy-Statement",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://www.police.gov.sg/Terms-of-Use",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://www.tech.gov.sg/report-vulnerability",target:"_blank",rel:"noopener",external:!0}},a={privacy:{href:"https://supportgowhere.life.gov.sg/privacy-statement.pdf",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://supportgowhere.life.gov.sg/terms-of-use.pdf",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://www.tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},s=s=>{switch(s){case"bookingsg":return r;case"imda":return t;case"mylegacy":return o;case"spf":return n;case"supportgowhere":return a;default:return e}};export{s as getDefaultDisclaimerLinks};
1
+ const e={privacy:{href:"https://www.life.gov.sg/privacy-statement",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://www.life.gov.sg/terms-of-use",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},r={privacy:{href:"https://assets.life.gov.sg/bookingsg/BSG_Privacy_Policy.pdf",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://assets.life.gov.sg/bookingsg/BSG_Terms_of_Use.pdf",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},t={privacy:{href:"https://www.imda.gov.sg/privacy-statement",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://www.imda.gov.sg/terms-of-use",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://www.tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},o={privacy:{href:"https://mylegacy.life.gov.sg/privacy-statement",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://mylegacy.life.gov.sg/terms-of-use",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},n={privacy:{href:"https://www.police.gov.sg/Privacy-Statement",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://www.police.gov.sg/Terms-of-Use",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://www.tech.gov.sg/report-vulnerability",target:"_blank",rel:"noopener",external:!0}},s={privacy:{href:"https://www.sportsingapore.gov.sg/privacy/",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://www.sportsingapore.gov.sg/terms-of-use/",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://www.tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},a={privacy:{href:"https://supportgowhere.life.gov.sg/privacy-statement.pdf",target:"_blank",rel:"noopener"},termsOfUse:{href:"https://supportgowhere.life.gov.sg/terms-of-use.pdf",target:"_blank",rel:"noopener"},reportVulnerability:{href:"https://www.tech.gov.sg/report_vulnerability",target:"_blank",rel:"noopener",external:!0}},l=l=>{switch(l){case"bookingsg":return r;case"imda":return t;case"mylegacy":return o;case"spf":return n;case"sportsg-orange":return s;case"supportgowhere":return a;default:return e}};export{l as getDefaultDisclaimerLinks};
2
2
  //# sourceMappingURL=footer-disclaimer-links-data.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"footer-disclaimer-links-data.js","sources":["../../src/footer/footer-disclaimer-links-data.tsx"],"sourcesContent":["import type { ThemeType } from \"../theme/types\";\nimport type { InternalDisclaimerLinks } from \"./footer-helper\";\n\nconst BaseDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://www.life.gov.sg/privacy-statement\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://www.life.gov.sg/terms-of-use\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst BookingSgDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://assets.life.gov.sg/bookingsg/BSG_Privacy_Policy.pdf\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://assets.life.gov.sg/bookingsg/BSG_Terms_of_Use.pdf\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst IMDADisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://www.imda.gov.sg/privacy-statement\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://www.imda.gov.sg/terms-of-use\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://www.tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst MyLegacyDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://mylegacy.life.gov.sg/privacy-statement\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://mylegacy.life.gov.sg/terms-of-use\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst SPFDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://www.police.gov.sg/Privacy-Statement\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://www.police.gov.sg/Terms-of-Use\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://www.tech.gov.sg/report-vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst SupportGoWhereDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://supportgowhere.life.gov.sg/privacy-statement.pdf\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://supportgowhere.life.gov.sg/terms-of-use.pdf\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://www.tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nexport const getDefaultDisclaimerLinks = (resourceScheme?: ThemeType) => {\n switch (resourceScheme) {\n case \"bookingsg\":\n return BookingSgDisclaimerLinks;\n case \"imda\":\n return IMDADisclaimerLinks;\n case \"mylegacy\":\n return MyLegacyDisclaimerLinks;\n case \"spf\":\n return SPFDisclaimerLinks;\n case \"supportgowhere\":\n return SupportGoWhereDisclaimerLinks;\n default:\n return BaseDisclaimerLinks;\n }\n};\n"],"names":["BaseDisclaimerLinks","privacy","href","target","rel","termsOfUse","reportVulnerability","external","BookingSgDisclaimerLinks","IMDADisclaimerLinks","MyLegacyDisclaimerLinks","SPFDisclaimerLinks","SupportGoWhereDisclaimerLinks","getDefaultDisclaimerLinks","resourceScheme"],"mappings":"AAGA,MAAMA,EAA+C,CACjDC,QAAS,CACLC,KAAM,4CACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,uCACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,2CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZC,EAAoD,CACtDP,QAAS,CACLC,KAAM,8DACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,4DACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,2CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZE,EAA+C,CACjDR,QAAS,CACLC,KAAM,4CACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,uCACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,+CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZG,EAAmD,CACrDT,QAAS,CACLC,KAAM,iDACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,4CACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,2CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZI,EAA8C,CAChDV,QAAS,CACLC,KAAM,8CACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,yCACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,+CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZK,EAAyD,CAC3DX,QAAS,CACLC,KAAM,2DACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,sDACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,+CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAILM,EAA6BC,IACtC,OAAQA,GACJ,IAAK,YACD,OAAON,EACX,IAAK,OACD,OAAOC,EACX,IAAK,WACD,OAAOC,EACX,IAAK,MACD,OAAOC,EACX,IAAK,iBACD,OAAOC,EACX,QACI,OAAOZ"}
1
+ {"version":3,"file":"footer-disclaimer-links-data.js","sources":["../../src/footer/footer-disclaimer-links-data.tsx"],"sourcesContent":["import type { ThemeType } from \"../theme/types\";\nimport type { InternalDisclaimerLinks } from \"./footer-helper\";\n\nconst BaseDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://www.life.gov.sg/privacy-statement\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://www.life.gov.sg/terms-of-use\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst BookingSgDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://assets.life.gov.sg/bookingsg/BSG_Privacy_Policy.pdf\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://assets.life.gov.sg/bookingsg/BSG_Terms_of_Use.pdf\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst IMDADisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://www.imda.gov.sg/privacy-statement\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://www.imda.gov.sg/terms-of-use\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://www.tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst MyLegacyDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://mylegacy.life.gov.sg/privacy-statement\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://mylegacy.life.gov.sg/terms-of-use\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst SPFDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://www.police.gov.sg/Privacy-Statement\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://www.police.gov.sg/Terms-of-Use\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://www.tech.gov.sg/report-vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst SportSGOrangeWhereDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://www.sportsingapore.gov.sg/privacy/\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://www.sportsingapore.gov.sg/terms-of-use/\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://www.tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nconst SupportGoWhereDisclaimerLinks: InternalDisclaimerLinks = {\n privacy: {\n href: \"https://supportgowhere.life.gov.sg/privacy-statement.pdf\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n termsOfUse: {\n href: \"https://supportgowhere.life.gov.sg/terms-of-use.pdf\",\n target: \"_blank\",\n rel: \"noopener\",\n },\n reportVulnerability: {\n href: \"https://www.tech.gov.sg/report_vulnerability\",\n target: \"_blank\",\n rel: \"noopener\",\n external: true,\n },\n};\n\nexport const getDefaultDisclaimerLinks = (resourceScheme?: ThemeType) => {\n switch (resourceScheme) {\n case \"bookingsg\":\n return BookingSgDisclaimerLinks;\n case \"imda\":\n return IMDADisclaimerLinks;\n case \"mylegacy\":\n return MyLegacyDisclaimerLinks;\n case \"spf\":\n return SPFDisclaimerLinks;\n case \"sportsg-orange\":\n return SportSGOrangeWhereDisclaimerLinks;\n case \"supportgowhere\":\n return SupportGoWhereDisclaimerLinks;\n default:\n return BaseDisclaimerLinks;\n }\n};\n"],"names":["BaseDisclaimerLinks","privacy","href","target","rel","termsOfUse","reportVulnerability","external","BookingSgDisclaimerLinks","IMDADisclaimerLinks","MyLegacyDisclaimerLinks","SPFDisclaimerLinks","SportSGOrangeWhereDisclaimerLinks","SupportGoWhereDisclaimerLinks","getDefaultDisclaimerLinks","resourceScheme"],"mappings":"AAGA,MAAMA,EAA+C,CACjDC,QAAS,CACLC,KAAM,4CACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,uCACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,2CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZC,EAAoD,CACtDP,QAAS,CACLC,KAAM,8DACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,4DACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,2CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZE,EAA+C,CACjDR,QAAS,CACLC,KAAM,4CACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,uCACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,+CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZG,EAAmD,CACrDT,QAAS,CACLC,KAAM,iDACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,4CACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,2CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZI,EAA8C,CAChDV,QAAS,CACLC,KAAM,8CACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,yCACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,+CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZK,EAA6D,CAC/DX,QAAS,CACLC,KAAM,6CACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,kDACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,+CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAIZM,EAAyD,CAC3DZ,QAAS,CACLC,KAAM,2DACNC,OAAQ,SACRC,IAAK,YAETC,WAAY,CACRH,KAAM,sDACNC,OAAQ,SACRC,IAAK,YAETE,oBAAqB,CACjBJ,KAAM,+CACNC,OAAQ,SACRC,IAAK,WACLG,UAAU,IAILO,EAA6BC,IACtC,OAAQA,GACJ,IAAK,YACD,OAAOP,EACX,IAAK,OACD,OAAOC,EACX,IAAK,WACD,OAAOC,EACX,IAAK,MACD,OAAOC,EACX,IAAK,iBACD,OAAOC,EACX,IAAK,iBACD,OAAOC,EACX,QACI,OAAOb"}
@@ -1,2 +1,2 @@
1
- import"./footer.styles_1msorhc.css";const l="b1ozupxm",s="f1b3ng0m",o="f1f7a2pi",t="t1nab4kr",e="l1t4stq",b="l14ev7f9",g={lifesg:"l1rsy8ww",bookingsg:"bsv4pll",mylegacy:"m1m4savg",ccube:"c6zlk1a",supportgowhere:"sdqdit1"},r="l1guit5j",c="l1dnmj9w",m="a1r2lyba",p="b1ni5lnl",a="bakpusz",n="cb1d27e",f="cntgl6w",i="d2vgmpj";export{m as addonSection,l as baseFooter,p as bottomSection,a as bottomSectionContent,n as copyrightSection,f as copyrightText,i as disclaimerTextLink,s as footerLink,o as fullWidthDivider,c as linkSection,r as linkSectionWrapper,b as logoImage,e as logoSection,g as logoSize,t as topSection};
1
+ import"./footer.styles_14hnlcq.css";const l="b1ozupxm",s="f1b3ng0m",t="f1f7a2pi",o="t1nab4kr",e="l1t4stq",b="l14ev7f9",g={bookingsg:"b1rsy8ww",ccube:"csv4pll",lifesg:"l1m4savg",mylegacy:"m6zlk1a",supportgowhere:"sdqdit1"},c="l1guit5j",n="l1dnmj9w",p="a1r2lyba",r="b1ni5lnl",a="bakpusz",m="cb1d27e",f="cntgl6w",i="d2vgmpj";export{p as addonSection,l as baseFooter,r as bottomSection,a as bottomSectionContent,m as copyrightSection,f as copyrightText,i as disclaimerTextLink,s as footerLink,t as fullWidthDivider,n as linkSection,c as linkSectionWrapper,b as logoImage,e as logoSection,g as logoSize,o as topSection};
2
2
  //# sourceMappingURL=footer.styles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"footer.styles.js","sources":["../../src/footer/footer.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport { ComponentToken, MediaQuery, Spacing } from \"../theme\";\nimport type { ThemeType } from \"../theme/types\";\n\n// =============================================================================\n// GENERIC\n// =============================================================================\n\nexport const baseFooter = css`\n background: ${ComponentToken.Footer[\"colour-bg\"]};\n`;\n\nexport const footerLink = css`\n color: ${ComponentToken.Footer[\"link-colour-text\"]};\n\n &:hover {\n color: ${ComponentToken.Footer[\"link-colour-text-hover\"]};\n }\n`;\n\n// =============================================================================\n// TOP SECTION\n// =============================================================================\n\nexport const fullWidthDivider = css`\n width: 100%;\n`;\n\nexport const topSection = css`\n padding: ${Spacing[\"spacing-64\"]} 0;\n\n ${MediaQuery.MaxWidth.lg} {\n padding: ${Spacing[\"spacing-32\"]} 0;\n\n [data-id=\"container\"] {\n flex-direction: column;\n gap: ${Spacing[\"spacing-32\"]};\n }\n }\n`;\n\nexport const logoSection = css`\n display: flex;\n width: fit-content;\n margin-right: ${Spacing[\"spacing-64\"]};\n\n ${MediaQuery.MaxWidth.lg} {\n width: 100%;\n margin-right: 0;\n }\n`;\n\nexport const logoImage = css`\n max-height: 4rem;\n max-width: 100%;\n object-fit: contain;\n`;\n\nexport const logoSize: Partial<Record<ThemeType, string>> = {\n lifesg: css`\n width: 4rem;\n `,\n bookingsg: css`\n width: 4rem;\n `,\n mylegacy: css`\n width: 10rem;\n max-height: 2rem;\n `,\n ccube: css`\n width: 10rem;\n `,\n supportgowhere: css`\n width: 10rem;\n `,\n};\n\nexport const linkSectionWrapper = css`\n display: flex;\n gap: ${Spacing[\"spacing-32\"]};\n flex: 1 1 auto;\n max-width: calc(32% + ${Spacing[\"spacing-32\"]});\n\n ${MediaQuery.MaxWidth.lg} {\n max-width: 100%;\n }\n`;\n\nexport const linkSection = css`\n flex: 1 1 50%;\n list-style-type: none;\n\n &:nth-of-type(2) {\n // 2nd col\n grid-column: 7 / span 4;\n }\n\n li {\n &:not(:last-child) {\n margin-bottom: ${Spacing[\"spacing-8\"]};\n }\n\n a {\n display: inline-block;\n }\n }\n\n ${MediaQuery.MaxWidth.lg} {\n // first col\n grid-column: 1 / span 6;\n\n &:nth-of-type(2) {\n // 2nd col\n grid-column: 7 / span 6;\n }\n }\n\n ${MediaQuery.MaxWidth.md} {\n // first col\n grid-column: 1 / span 4;\n\n &:nth-of-type(2) {\n // 2nd col\n grid-column: 5 / span 4;\n }\n }\n`;\n\nexport const addonSection = css`\n flex: 0 1 33%;\n margin-left: auto;\n\n ${MediaQuery.MaxWidth.lg} {\n flex: 1 1 100%;\n margin-left: 0;\n }\n`;\n\n// =============================================================================\n// BOTTOM SECTION\n// =============================================================================\n\nexport const bottomSection = css`\n padding: ${Spacing[\"spacing-20\"]} 0;\n\n [data-id=\"container\"] {\n column-gap: ${Spacing[\"spacing-32\"]};\n }\n\n ${MediaQuery.MaxWidth.lg} {\n border-top: none;\n padding: ${Spacing[\"spacing-16\"]} 0;\n\n [data-id=\"container\"] {\n flex-direction: column;\n }\n }\n`;\n\nexport const bottomSectionContent = css`\n flex: 1 1 50%;\n display: flex;\n\n ${MediaQuery.MaxWidth.lg} {\n flex-direction: column;\n gap: ${Spacing[\"spacing-8\"]};\n &:not(:last-child) {\n margin-right: 0;\n }\n }\n`;\n\nexport const copyrightSection = css`\n justify-content: flex-end;\n\n ${MediaQuery.MaxWidth.lg} {\n margin-top: ${Spacing[\"spacing-16\"]};\n justify-content: flex-start;\n }\n`;\n\nexport const copyrightText = css`\n color: ${ComponentToken.Footer[\"colour-text\"]};\n`;\n\nexport const disclaimerTextLink = css`\n color: ${ComponentToken.Footer[\"disclaimer-link-colour-text\"]};\n &:not(:last-child) {\n margin-right: ${Spacing[\"spacing-16\"]};\n }\n\n &:hover {\n color: ${ComponentToken.Footer[\"disclaimer-link-colour-text-hover\"]};\n }\n\n ${MediaQuery.MaxWidth.lg} {\n &:not(:last-child) {\n margin-right: 0;\n }\n }\n`;\n"],"names":["baseFooter","footerLink","fullWidthDivider","topSection","logoSection","logoImage","logoSize","lifesg","bookingsg","mylegacy","ccube","supportgowhere","linkSectionWrapper","linkSection","addonSection","bottomSection","bottomSectionContent","copyrightSection","copyrightText","disclaimerTextLink"],"mappings":"0DASO,MAAMA,EAAU,WAIVC,EAAU,WAYVC,EAAgB,WAIhBC,EAAU,WAaVC,EAAW,UAWXC,EAAS,WAMTC,EAA+C,CACxDC,OAAM,WAGNC,UAAS,UAGTC,SAAQ,WAIRC,MAAK,UAGLC,eAAc,WAKLC,EAAkB,WAWlBC,EAAW,WAwCXC,EAAY,WAcZC,EAAa,WAiBbC,EAAoB,UAapBC,EAAgB,UAShBC,EAAa,UAIbC,EAAkB"}
1
+ {"version":3,"file":"footer.styles.js","sources":["../../src/footer/footer.styles.ts"],"sourcesContent":["import { css } from \"@linaria/core\";\n\nimport { ComponentToken, MediaQuery, Spacing } from \"../theme\";\nimport type { ThemeType } from \"../theme/types\";\n\n// =============================================================================\n// GENERIC\n// =============================================================================\n\nexport const baseFooter = css`\n background: ${ComponentToken.Footer[\"colour-bg\"]};\n`;\n\nexport const footerLink = css`\n color: ${ComponentToken.Footer[\"link-colour-text\"]};\n\n &:hover {\n color: ${ComponentToken.Footer[\"link-colour-text-hover\"]};\n }\n`;\n\n// =============================================================================\n// TOP SECTION\n// =============================================================================\n\nexport const fullWidthDivider = css`\n width: 100%;\n`;\n\nexport const topSection = css`\n padding: ${Spacing[\"spacing-64\"]} 0;\n\n ${MediaQuery.MaxWidth.lg} {\n padding: ${Spacing[\"spacing-32\"]} 0;\n\n [data-id=\"container\"] {\n flex-direction: column;\n gap: ${Spacing[\"spacing-32\"]};\n }\n }\n`;\n\nexport const logoSection = css`\n display: flex;\n width: fit-content;\n margin-right: ${Spacing[\"spacing-64\"]};\n\n ${MediaQuery.MaxWidth.lg} {\n width: 100%;\n margin-right: 0;\n }\n`;\n\nexport const logoImage = css`\n max-height: 4rem;\n max-width: 100%;\n object-fit: contain;\n`;\n\nexport const logoSize: Partial<Record<ThemeType, string>> = {\n bookingsg: css`\n width: 4rem;\n `,\n ccube: css`\n width: 10rem;\n `,\n lifesg: css`\n width: 4rem;\n `,\n mylegacy: css`\n width: 10rem;\n max-height: 2rem;\n `,\n supportgowhere: css`\n width: 10rem;\n `,\n};\n\nexport const linkSectionWrapper = css`\n display: flex;\n gap: ${Spacing[\"spacing-32\"]};\n flex: 1 1 auto;\n max-width: calc(32% + ${Spacing[\"spacing-32\"]});\n\n ${MediaQuery.MaxWidth.lg} {\n max-width: 100%;\n }\n`;\n\nexport const linkSection = css`\n flex: 1 1 50%;\n list-style-type: none;\n\n &:nth-of-type(2) {\n // 2nd col\n grid-column: 7 / span 4;\n }\n\n li {\n &:not(:last-child) {\n margin-bottom: ${Spacing[\"spacing-8\"]};\n }\n\n a {\n display: inline-block;\n }\n }\n\n ${MediaQuery.MaxWidth.lg} {\n // first col\n grid-column: 1 / span 6;\n\n &:nth-of-type(2) {\n // 2nd col\n grid-column: 7 / span 6;\n }\n }\n\n ${MediaQuery.MaxWidth.md} {\n // first col\n grid-column: 1 / span 4;\n\n &:nth-of-type(2) {\n // 2nd col\n grid-column: 5 / span 4;\n }\n }\n`;\n\nexport const addonSection = css`\n flex: 0 1 33%;\n margin-left: auto;\n\n ${MediaQuery.MaxWidth.lg} {\n flex: 1 1 100%;\n margin-left: 0;\n }\n`;\n\n// =============================================================================\n// BOTTOM SECTION\n// =============================================================================\n\nexport const bottomSection = css`\n padding: ${Spacing[\"spacing-20\"]} 0;\n\n [data-id=\"container\"] {\n column-gap: ${Spacing[\"spacing-32\"]};\n }\n\n ${MediaQuery.MaxWidth.lg} {\n border-top: none;\n padding: ${Spacing[\"spacing-16\"]} 0;\n\n [data-id=\"container\"] {\n flex-direction: column;\n }\n }\n`;\n\nexport const bottomSectionContent = css`\n flex: 1 1 50%;\n display: flex;\n\n ${MediaQuery.MaxWidth.lg} {\n flex-direction: column;\n gap: ${Spacing[\"spacing-8\"]};\n &:not(:last-child) {\n margin-right: 0;\n }\n }\n`;\n\nexport const copyrightSection = css`\n justify-content: flex-end;\n\n ${MediaQuery.MaxWidth.lg} {\n margin-top: ${Spacing[\"spacing-16\"]};\n justify-content: flex-start;\n }\n`;\n\nexport const copyrightText = css`\n color: ${ComponentToken.Footer[\"colour-text\"]};\n`;\n\nexport const disclaimerTextLink = css`\n color: ${ComponentToken.Footer[\"disclaimer-link-colour-text\"]};\n &:not(:last-child) {\n margin-right: ${Spacing[\"spacing-16\"]};\n }\n\n &:hover {\n color: ${ComponentToken.Footer[\"disclaimer-link-colour-text-hover\"]};\n }\n\n ${MediaQuery.MaxWidth.lg} {\n &:not(:last-child) {\n margin-right: 0;\n }\n }\n`;\n"],"names":["baseFooter","footerLink","fullWidthDivider","topSection","logoSection","logoImage","logoSize","bookingsg","ccube","lifesg","mylegacy","supportgowhere","linkSectionWrapper","linkSection","addonSection","bottomSection","bottomSectionContent","copyrightSection","copyrightText","disclaimerTextLink"],"mappings":"0DASO,MAAMA,EAAU,WAIVC,EAAU,WAYVC,EAAgB,WAIhBC,EAAU,WAaVC,EAAW,UAWXC,EAAS,WAMTC,EAA+C,CACxDC,UAAS,WAGTC,MAAK,UAGLC,OAAM,WAGNC,SAAQ,UAIRC,eAAc,WAKLC,EAAkB,WAWlBC,EAAW,WAwCXC,EAAY,WAcZC,EAAa,WAiBbC,EAAoB,UAapBC,EAAgB,UAShBC,EAAa,UAIbC,EAAkB"}
@@ -4,10 +4,10 @@
4
4
  .t1nab4kr{padding:var(--fds-spacing-64) 0;}:where(body.fds-breakpoint-lg-max) .t1nab4kr{padding:var(--fds-spacing-32) 0;}:where(body.fds-breakpoint-lg-max) .t1nab4kr [data-id="container"]{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:var(--fds-spacing-32);}
5
5
  .l1t4stq{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin-right:var(--fds-spacing-64);}:where(body.fds-breakpoint-lg-max) .l1t4stq{width:100%;margin-right:0;}
6
6
  .l14ev7f9{max-height:4rem;max-width:100%;object-fit:contain;}
7
- .l1rsy8ww{width:4rem;}
8
- .bsv4pll{width:4rem;}
9
- .m1m4savg{width:10rem;max-height:2rem;}
10
- .c6zlk1a{width:10rem;}
7
+ .b1rsy8ww{width:4rem;}
8
+ .csv4pll{width:10rem;}
9
+ .l1m4savg{width:4rem;}
10
+ .m6zlk1a{width:10rem;max-height:2rem;}
11
11
  .sdqdit1{width:10rem;}
12
12
  .l1guit5j{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:var(--fds-spacing-32);-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto;max-width:calc(32% + var(--fds-spacing-32));}:where(body.fds-breakpoint-lg-max) .l1guit5j{max-width:100%;}
13
13
  .l1dnmj9w{-webkit-flex:1 1 50%;-ms-flex:1 1 50%;flex:1 1 50%;list-style-type:none;}.l1dnmj9w:nth-of-type(2){-ms-grid-column:7;-ms-grid-column-span:4;grid-column:7/span 4;}.l1dnmj9w li:not(:last-child){margin-bottom:var(--fds-spacing-8);}.l1dnmj9w li a{display:inline-block;}:where(body.fds-breakpoint-lg-max) .l1dnmj9w{-ms-grid-column:1;-ms-grid-column-span:6;grid-column:1/span 6;}:where(body.fds-breakpoint-lg-max) .l1dnmj9w:nth-of-type(2){-ms-grid-column:7;-ms-grid-column-span:6;grid-column:7/span 6;}:where(body.fds-breakpoint-md-max) .l1dnmj9w{-ms-grid-column:1;-ms-grid-column-span:4;grid-column:1/span 4;}:where(body.fds-breakpoint-md-max) .l1dnmj9w:nth-of-type(2){-ms-grid-column:5;-ms-grid-column-span:4;grid-column:5/span 4;}
@@ -1,5 +1,5 @@
1
1
  import type { FullscreenImageCarouselProps, FullscreenImageCarouselRef } from "./types";
2
- export declare const Component: ({ items, initialActiveItemIndex, hideThumbnail, hideNavigation, hideCounter, hideMagnifier, onDelete, onClose, insets, show, ...otherProps }: FullscreenImageCarouselProps, ref: React.Ref<FullscreenImageCarouselRef>) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const Component: ({ items, initialActiveItemIndex, hideThumbnail, hideNavigation, hideCounter, hideMagnifier, customActions, onDelete, onClose, insets, topBarRef, show, ...otherProps }: FullscreenImageCarouselProps, ref: React.Ref<FullscreenImageCarouselRef>) => import("react/jsx-runtime").JSX.Element;
3
3
  /**
4
4
  * A fullscreen modal carousel for browsing images and custom media items.
5
5
  *
@@ -1,2 +1,2 @@
1
- import{__rest as e}from"../_virtual/_tslib.js";import{jsx as i,jsxs as t,Fragment as l}from"react/jsx-runtime";import{ChevronLeftIcon as r,ChevronRightIcon as o,MagnifierPlusIcon as a,MagnifierMinusIcon as n,CrossIcon as s}from"@lifesg/react-icons";import{BinIcon as d}from"@lifesg/react-icons/bin";import{clearAnnouncer as c,announce as m}from"@react-aria/live-announcer";import u from"clsx";import{forwardRef as h,useState as v,useRef as p,useMemo as f,useCallback as g,useImperativeHandle as b,useEffect as N}from"react";import{TransformWrapper as j,TransformComponent as x}from"react-zoom-pan-pinch";import{ModalV2 as k}from"../modal-v2/modal-v2.js";import{ClickableIcon as y}from"../shared/clickable-icon/clickable-icon.js";import{useStateCallback as w}from"../shared/hooks/use-state-callback.js";import{ImagePlaceholder as C}from"../shared/image-placeholder/image-placeholder.js";import"../theme/theme-provider/index.js";import{formatUnitValue as I}from"../theme/utils/css-variable.js";import"../theme/theme-provider/context.js";import"../theme/tokens/border.js";import"../theme/tokens/colour.js";import"../theme/tokens/font.js";import"../theme/tokens/media-query.js";import{useApplyStyle as T}from"../theme/utils/use-apply-styles.js";import"../theme/utils/use-media-query.js";import{Typography as $}from"../typography/index.js";import"../util/calendar-helper.js";import"../external/dayjs/dayjs.min.js";import"../util/simple-id-generator.js";import"../util/string-helper.js";import{useEventListener as B}from"../util/use-event-listener.js";import{tokens as S,carouselModalContent as A,imageGalleryContainer as L,imageGalleryWrapper as D,imageGallerySwipe as M,iconButton as z,arrowButton as X,arrowButtonLeft as Z,arrowButtonRight as O,boxChip as R,chip as W,topActionButtons as q,iconButtonError as E,imageGallerySlides as P,imageGallerySlide as V,focusableImageRegion as _,slidePlaceholderImage as F,slideImage as H,thumbnailContainer as G,thumbnailWrapper as J,thumbnailItemContainer as K,thumbnailItem as Q,thumbnailImage as U,fileInfoFileName as Y,fileInfoFileSize as ee,fileInfoTextWrapper as ie,fileInfoTextWrapperCentered as te}from"./fullscreen-image-carousel.styles.js";import{StatefulImage as le}from"./stateful-image.js";const re=e=>!!e&&"custom"===e.type,oe=(h,oe)=>{var ae,{items:ne,initialActiveItemIndex:se,hideThumbnail:de=!1,hideNavigation:ce=!1,hideCounter:me=!1,hideMagnifier:ue=!1,onDelete:he,onClose:ve,insets:pe,show:fe}=h,ge=e(h,["items","initialActiveItemIndex","hideThumbnail","hideNavigation","hideCounter","hideMagnifier","onDelete","onClose","insets","show"]);const[be,Ne]=w(null!=se?se:0),[je,xe]=v({}),[ke,ye]=v(1),[we,Ce]=v(),[Ie,Te]=v(),$e=p(null),Be=p([]),Se=p([]),Ae=p(null),Le=p(null),De=p(null),Me=p(null),ze=p(null),Xe=we&&Ie?we-Ie:0,Ze=ne[be],Oe=ne.some((e=>{var i;return re(e)&&!!(null===(i=e.itemLabel)||void 0===i?void 0:i.trim())})),Re=Oe?"item":"image",We=f((()=>ne.some((e=>{var i,t;return(null===(i=e.fileName)||void 0===i?void 0:i.trim())||(null===(t=e.fileSize)||void 0===t?void 0:t.trim())}))),[ne]);T(Le,{[S.topActionButtons.insetTop]:I(null==pe?void 0:pe.top,"px"),[S.topActionButtons.insetLeft]:I(null==pe?void 0:pe.left,"px"),[S.topActionButtons.insetRight]:I(null==pe?void 0:pe.right,"px")}),T(De,{[S.arrowButton.insetLeft]:I(null==pe?void 0:pe.left,"px")}),T(Me,{[S.arrowButton.insetRight]:I(null==pe?void 0:pe.right,"px")}),T(ze,{[S.thumbnailContainer.insetBottom]:I(null==pe?void 0:pe.bottom,"px")});const qe=g((e=>{var i,t;const l=ne[e],r=Oe?"Item":"Image",o=re(l)?(null===(i=l.itemLabel)||void 0===i?void 0:i.trim())||"":(null===(t=l.alt)||void 0===t?void 0:t.trim())||"",a=`${r} ${e+1} of ${ne.length}.`;return o?`${o}. ${a}`:a}),[ne,Oe]);b(oe,(()=>({currentItemIndex:be,setCurrentItem:He,goToPrevItem:_e,goToNextItem:Fe}))),B("keydown",(function(e){"ArrowRight"===e.key?Fe():"ArrowLeft"===e.key?_e():"Escape"===e.key&&(null==ve||ve())}),"document"),N((()=>{var e;fe&&(null===(e=Ae.current)||void 0===e||e.focus())}),[fe]),N((()=>{0!==ne.length?be>ne.length-1&&Ne(ne.length-1):Ne(0)}),[be,ne.length,Ne]),N((()=>{var e,i;null===(i=null===(e=Be.current)||void 0===e?void 0:e[be])||void 0===i||i.scrollIntoView({behavior:"smooth",inline:"center"}),ye(1)}),[be]);const Ee=e=>{ye(e.state.scale)};const Pe=({src:e,height:i,width:t})=>{xe((l=>Object.assign(Object.assign({},l),{[e]:{height:i,width:t}})))},Ve=()=>{if(!Ze||re(Ze))return;const e=je[Ze.src];if($e.current&&e){const{clientHeight:i,clientWidth:t}=$e.current,{width:l,height:r}=e,o=r/l<i/t;return l<t&&r<i?o?t/l:i/r:o?i/(r/(l/t)):t/(l/(r/i))}},_e=()=>{var e,i;null===(i=null===(e=Se.current)||void 0===e?void 0:e[be])||void 0===i||i.resetTransform(),Ne((e=>0===e?ne.length-1:e-1),(e=>{c("polite"),m(qe(e),"polite")}))},Fe=()=>{var e,i;null===(i=null===(e=Se.current)||void 0===e?void 0:e[be])||void 0===i||i.resetTransform(),Ne((e=>e===ne.length-1?0:e+1),(e=>{c("polite"),m(qe(e),"polite")}))},He=e=>{var i,t;null===(t=null===(i=Se.current)||void 0===i?void 0:i[be])||void 0===t||t.resetTransform(),Ne(e)};return i(k,Object.assign({},ge,{"data-testid":"image-carousel-modal","aria-label":Oe?"Carousel":"Image carousel",show:fe,disableInitialFocus:!0,children:t("div",{className:A,children:[t("div",{className:L,children:[t("div",{className:D,children:[i("div",{className:M,ref:$e,onTouchStart:e=>{ke<=1&&Ce(e.touches[0].clientX)},onTouchMove:e=>{!we||ke>1||Te(e.touches[0].clientX)},onTouchEnd:()=>{$e.current&&(Math.abs(Xe)>$e.current.offsetWidth/3&&(Xe>0?Fe():_e()),Ce(void 0),Te(void 0))},children:i("div",{className:P,style:{transform:`translateX(calc(${100*-be}% - ${Xe}px))`},children:ne.map(((e,t)=>{const l=t===be,r=Math.abs(t-be)<=1||0===be&&t===ne.length-1||be===ne.length-1&&0===t;return i("div",{className:V,"data-testid":"slide-item",children:i("div",{className:_,ref:l?Ae:null,tabIndex:l?0:-1,children:re(e)?r?e.renderContent():i(C,{className:F}):i(j,{ref:e=>Se.current[t]=e,panning:{disabled:ke<=1},initialScale:1,onZoom:Ee,onZoomStop:Ee,onWheel:Ee,children:i(x,{children:i(le,{className:H,src:e.src,alt:qe(t),placeholder:i(C,{className:F}),fit:"scale-down",retrieveImageDimension:!0,setDimension:Pe})})})})},t)}))})}),!ce&&t(l,{children:[i(y,{className:u(z,X,Z),ref:De,"aria-label":`Previous ${Re}`,"data-testid":"prev-btn",onClick:_e,children:i(r,{"aria-hidden":!0})}),i(y,{className:u(z,X,O),ref:Me,"aria-label":`Next ${Re}`,"data-testid":"forward-btn",onClick:Fe,children:i(o,{"aria-hidden":!0})})]}),!me&&i("div",{className:R,"aria-hidden":"true",children:i($.BodyXS,{className:W,weight:"semibold","data-testid":"carousel-counter",children:`${be+1}/${ne.length}`})})]}),!de&&i("div",{className:G,ref:ze,"aria-hidden":"true","data-testid":"thumbnail-container",children:i("div",{className:J,children:ne.map(((e,t)=>{var l;const r=re(e)?e.thumbnailSrc:null!==(l=e.thumbnailSrc)&&void 0!==l?l:e.src;return i("div",{className:K,children:i("div",{className:Q,"data-active":t===be,"data-testid":"thumbnail-item",onClick:()=>He(t),ref:e=>Be.current[t]=e,children:r?i(le,{className:U,src:r,alt:`Thumbnail ${t+1}`,fit:"cover"}):i(C,{className:F})})},t)}))})})]}),t("div",{className:q,ref:Le,"data-has-file-info":We,children:[We&&(()=>{const{fileName:e,fileSize:l}=null!=Ze?Ze:{},r=null==e?void 0:e.trim(),o=null==l?void 0:l.trim();return t("div",{className:u(ie,!o&&te),"aria-live":"polite","aria-atomic":"true","data-testid":"file-info-bar",children:[r&&i($.BodyBL,{className:Y,weight:"semibold","data-testid":"file-info-name",children:r}),o&&i($.BodyMD,{className:ee,"data-testid":"file-info-size",children:o})]})})(),!ue&&!re(Ze)&&i(y,{className:z,"aria-label":1===ke?"Zoom in":"Zoom out",onClick:()=>{var e,i,t,l;if(1===ke){const t=Ve();null===(i=null===(e=Se.current)||void 0===e?void 0:e[be])||void 0===i||i.centerView(t),ye(null!=t?t:1)}else ye(1),null===(l=null===(t=Se.current)||void 0===t?void 0:t[be])||void 0===l||l.resetTransform()},children:i(1===ke?a:n,{"aria-hidden":!0})}),he&&i(y,{className:u(z,E),"aria-label":`Delete ${re(Ze)&&(null===(ae=Ze.itemLabel)||void 0===ae?void 0:ae.trim())||"image"}`,"data-testid":"delete-btn",onClick:()=>{Ze&&he&&he(Ze,be)},children:i(d,{"aria-hidden":!0})}),i(y,{className:z,"aria-label":Oe?"Close carousel":"Close image carousel",onClick:ve,children:i(s,{"aria-hidden":!0})})]})]})}))},ae=h(oe);export{oe as Component,ae as FullscreenImageCarousel};
1
+ import{__rest as e}from"../_virtual/_tslib.js";import{jsx as i,jsxs as t,Fragment as l}from"react/jsx-runtime";import{ChevronLeftIcon as r,ChevronRightIcon as o,MagnifierPlusIcon as a,MagnifierMinusIcon as n,CrossIcon as s}from"@lifesg/react-icons";import{BinIcon as d}from"@lifesg/react-icons/bin";import{clearAnnouncer as c,announce as m}from"@react-aria/live-announcer";import u from"clsx";import{forwardRef as h,useState as v,useRef as p,useMemo as f,useCallback as g,useImperativeHandle as b,useEffect as N,cloneElement as j}from"react";import{TransformWrapper as k,TransformComponent as x}from"react-zoom-pan-pinch";import{ModalV2 as y}from"../modal-v2/modal-v2.js";import{ClickableIcon as C}from"../shared/clickable-icon/clickable-icon.js";import{useStateCallback as w}from"../shared/hooks/use-state-callback.js";import{ImagePlaceholder as I}from"../shared/image-placeholder/image-placeholder.js";import"../theme/theme-provider/index.js";import{formatUnitValue as T}from"../theme/utils/css-variable.js";import"../theme/theme-provider/context.js";import"../theme/tokens/border.js";import"../theme/tokens/colour.js";import"../theme/tokens/font.js";import"../theme/tokens/media-query.js";import{useApplyStyle as $}from"../theme/utils/use-apply-styles.js";import"../theme/utils/use-media-query.js";import{Typography as B}from"../typography/index.js";import"../util/calendar-helper.js";import"../external/dayjs/dayjs.min.js";import{mergeRefs as A}from"../util/merge-refs.js";import"../util/simple-id-generator.js";import"../util/string-helper.js";import{useEventListener as L}from"../util/use-event-listener.js";import{tokens as S,carouselModalContent as D,imageGalleryContainer as M,imageGalleryWrapper as R,imageGallerySwipe as z,iconButton as X,arrowButton as Z,arrowButtonLeft as O,arrowButtonRight as W,boxChip as q,chip as E,imageGallerySlides as P,imageGallerySlide as V,focusableImageRegion as _,slidePlaceholderImage as F,slideImage as H,thumbnailContainer as G,thumbnailWrapper as J,thumbnailItemContainer as K,thumbnailItem as Q,thumbnailImage as U,topActionButtons as Y,iconButtonError as ee,fileInfoFileName as ie,fileInfoFileSize as te,fileInfoTextWrapper as le,fileInfoTextWrapperCentered as re}from"./fullscreen-image-carousel.styles.js";import{StatefulImage as oe}from"./stateful-image.js";const ae=e=>!!e&&"custom"===e.type,ne=(h,ne)=>{var se,de,{items:ce,initialActiveItemIndex:me,hideThumbnail:ue=!1,hideNavigation:he=!1,hideCounter:ve=!1,hideMagnifier:pe=!1,customActions:fe,onDelete:ge,onClose:be,insets:Ne,topBarRef:je,show:ke}=h,xe=e(h,["items","initialActiveItemIndex","hideThumbnail","hideNavigation","hideCounter","hideMagnifier","customActions","onDelete","onClose","insets","topBarRef","show"]);const[ye,Ce]=w(null!=me?me:0),[we,Ie]=v({}),[Te,$e]=v(1),[Be,Ae]=v(),[Le,Se]=v(),De=p(null),Me=p([]),Re=p([]),ze=p(null),[Xe,Ze]=v(null),Oe=f((()=>({current:Xe})),[Xe]),We=f((()=>A(Ze,je)),[je]),qe=p(null),Ee=p(null),Pe=p(null),Ve=Be&&Le?Be-Le:0,_e=ce[ye],Fe=null!==(de=null!==(se=null==_e?void 0:_e.customActions)&&void 0!==se?se:fe)&&void 0!==de?de:[],He=ce.some((e=>{var i;return ae(e)&&!!(null===(i=e.itemLabel)||void 0===i?void 0:i.trim())})),Ge=He?"item":"image",Je=f((()=>ce.some((e=>{var i,t;return(null===(i=e.fileName)||void 0===i?void 0:i.trim())||(null===(t=e.fileSize)||void 0===t?void 0:t.trim())}))),[ce]);$(Oe,{[S.topActionButtons.insetTop]:T(null==Ne?void 0:Ne.top,"px"),[S.topActionButtons.insetLeft]:T(null==Ne?void 0:Ne.left,"px"),[S.topActionButtons.insetRight]:T(null==Ne?void 0:Ne.right,"px")}),$(qe,{[S.arrowButton.insetLeft]:T(null==Ne?void 0:Ne.left,"px")}),$(Ee,{[S.arrowButton.insetRight]:T(null==Ne?void 0:Ne.right,"px")}),$(Pe,{[S.thumbnailContainer.insetBottom]:T(null==Ne?void 0:Ne.bottom,"px")});const Ke=g((e=>{var i,t;const l=ce[e],r=He?"Item":"Image",o=ae(l)?(null===(i=l.itemLabel)||void 0===i?void 0:i.trim())||"":(null===(t=l.alt)||void 0===t?void 0:t.trim())||"",a=`${r} ${e+1} of ${ce.length}.`;return o?`${o}. ${a}`:a}),[ce,He]);b(ne,(()=>({currentItemIndex:ye,setCurrentItem:ri,goToPrevItem:ti,goToNextItem:li}))),L("keydown",(function(e){"ArrowRight"===e.key?li():"ArrowLeft"===e.key?ti():"Escape"===e.key&&(null==be||be())}),"document"),N((()=>{var e;ke&&(null===(e=ze.current)||void 0===e||e.focus())}),[ke]),N((()=>{0!==ce.length?ye>ce.length-1&&Ce(ce.length-1):Ce(0)}),[ye,ce.length,Ce]),N((()=>{var e,i;null===(i=null===(e=Me.current)||void 0===e?void 0:e[ye])||void 0===i||i.scrollIntoView({behavior:"smooth",inline:"center"}),$e(1)}),[ye]);const Qe=e=>{$e(e.state.scale)},Ue=()=>{_e&&ge&&ge(_e,ye)};const Ye=()=>{var e,i,t,l;if(1===Te){const t=ii();null===(i=null===(e=Re.current)||void 0===e?void 0:e[ye])||void 0===i||i.centerView(t),$e(null!=t?t:1)}else $e(1),null===(l=null===(t=Re.current)||void 0===t?void 0:t[ye])||void 0===l||l.resetTransform()},ei=({src:e,height:i,width:t})=>{Ie((l=>Object.assign(Object.assign({},l),{[e]:{height:i,width:t}})))},ii=()=>{if(!_e||ae(_e))return;const e=we[_e.src];if(De.current&&e){const{clientHeight:i,clientWidth:t}=De.current,{width:l,height:r}=e,o=r/l<i/t;return l<t&&r<i?o?t/l:i/r:o?i/(r/(l/t)):t/(l/(r/i))}},ti=()=>{var e,i;null===(i=null===(e=Re.current)||void 0===e?void 0:e[ye])||void 0===i||i.resetTransform(),Ce((e=>0===e?ce.length-1:e-1),(e=>{c("polite"),m(Ke(e),"polite")}))},li=()=>{var e,i;null===(i=null===(e=Re.current)||void 0===e?void 0:e[ye])||void 0===i||i.resetTransform(),Ce((e=>e===ce.length-1?0:e+1),(e=>{c("polite"),m(Ke(e),"polite")}))},ri=e=>{var i,t;null===(t=null===(i=Re.current)||void 0===i?void 0:i[ye])||void 0===t||t.resetTransform(),Ce(e)},oi=()=>{const{fileName:e,fileSize:l}=null!=_e?_e:{},r=null==e?void 0:e.trim(),o=null==l?void 0:l.trim();return t("div",{className:u(le,!o&&re),"aria-live":"polite","aria-atomic":"true","data-testid":"file-info-bar",children:[r&&i(B.BodyBL,{className:ie,weight:"semibold","data-testid":"file-info-name",children:r}),o&&i(B.BodyMD,{className:te,"data-testid":"file-info-size",children:o})]})};return i(y,Object.assign({},xe,{"data-testid":"image-carousel-modal","aria-label":He?"Carousel":"Image carousel",show:ke,disableInitialFocus:!0,children:t("div",{className:D,children:[t("div",{className:M,children:[t("div",{className:R,children:[i("div",{className:z,ref:De,onTouchStart:e=>{Te<=1&&Ae(e.touches[0].clientX)},onTouchMove:e=>{!Be||Te>1||Se(e.touches[0].clientX)},onTouchEnd:()=>{De.current&&(Math.abs(Ve)>De.current.offsetWidth/3&&(Ve>0?li():ti()),Ae(void 0),Se(void 0))},children:i("div",{className:P,style:{transform:`translateX(calc(${100*-ye}% - ${Ve}px))`},children:ce.map(((e,t)=>{const l=t===ye,r=Math.abs(t-ye)<=1||0===ye&&t===ce.length-1||ye===ce.length-1&&0===t;return i("div",{className:V,"data-testid":"slide-item",children:i("div",{className:_,ref:l?ze:null,tabIndex:l?0:-1,children:ae(e)?r?e.renderContent():i(I,{className:F}):i(k,{ref:e=>Re.current[t]=e,panning:{disabled:Te<=1},initialScale:1,onZoom:Qe,onZoomStop:Qe,onWheel:Qe,children:i(x,{children:i(oe,{className:H,src:e.src,alt:Ke(t),placeholder:i(I,{className:F}),fit:"scale-down",retrieveImageDimension:!0,setDimension:ei})})})})},t)}))})}),!he&&t(l,{children:[i(C,{className:u(X,Z,O),ref:qe,"aria-label":`Previous ${Ge}`,"data-testid":"prev-btn",onClick:ti,children:i(r,{"aria-hidden":!0})}),i(C,{className:u(X,Z,W),ref:Ee,"aria-label":`Next ${Ge}`,"data-testid":"forward-btn",onClick:li,children:i(o,{"aria-hidden":!0})})]}),!ve&&i("div",{className:q,"aria-hidden":"true",children:i(B.BodyXS,{className:E,weight:"semibold","data-testid":"carousel-counter",children:`${ye+1}/${ce.length}`})})]}),!ue&&i("div",{className:G,ref:Pe,"aria-hidden":"true","data-testid":"thumbnail-container",children:i("div",{className:J,children:ce.map(((e,t)=>{var l;const r=ae(e)?e.thumbnailSrc:null!==(l=e.thumbnailSrc)&&void 0!==l?l:e.src;return i("div",{className:K,children:i("div",{className:Q,"data-active":t===ye,"data-testid":"thumbnail-item",onClick:()=>ri(t),ref:e=>Me.current[t]=e,children:r?i(oe,{className:U,src:r,alt:`Thumbnail ${t+1}`,fit:"cover"}):i(I,{className:F})})},t)}))})})]}),(()=>{var e;return t("div",{className:Y,ref:We,"data-has-file-info":Je,children:[Je&&oi(),Fe.map(((e,t)=>i(C,{className:X,"aria-label":e.ariaLabel,"data-testid":e["data-testid"],onClick:()=>(e=>{_e&&e.onClick(_e,ye)})(e),children:j(e.icon,{"aria-hidden":!0})},t))),!pe&&!ae(_e)&&i(C,{className:X,"aria-label":1===Te?"Zoom in":"Zoom out",onClick:Ye,children:i(1===Te?a:n,{"aria-hidden":!0})}),ge&&i(C,{className:u(X,ee),"aria-label":`Delete ${ae(_e)&&(null===(e=_e.itemLabel)||void 0===e?void 0:e.trim())||"image"}`,"data-testid":"delete-btn",onClick:Ue,children:i(d,{"aria-hidden":!0})}),i(C,{className:X,"aria-label":He?"Close carousel":"Close image carousel",onClick:be,children:i(s,{"aria-hidden":!0})})]})})()]})}))},se=h(ne);export{ne as Component,se as FullscreenImageCarousel};
2
2
  //# sourceMappingURL=fullscreen-image-carousel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fullscreen-image-carousel.js","sources":["../../src/fullscreen-image-carousel/fullscreen-image-carousel.tsx"],"sourcesContent":["import {\n ChevronLeftIcon,\n ChevronRightIcon,\n CrossIcon,\n MagnifierMinusIcon,\n MagnifierPlusIcon,\n} from \"@lifesg/react-icons\";\nimport { BinIcon } from \"@lifesg/react-icons/bin\";\nimport { announce, clearAnnouncer } from \"@react-aria/live-announcer\";\nimport clsx from \"clsx\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type {\n ReactZoomPanPinchContentRef,\n ReactZoomPanPinchRef,\n} from \"react-zoom-pan-pinch\";\nimport { TransformComponent, TransformWrapper } from \"react-zoom-pan-pinch\";\n\nimport { ModalV2 } from \"../modal-v2\";\nimport { ClickableIcon } from \"../shared/clickable-icon\";\nimport { useStateCallback } from \"../shared/hooks\";\nimport { ImagePlaceholder } from \"../shared/image-placeholder\";\nimport { formatUnitValue, useApplyStyle } from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { useEventListener } from \"../util\";\nimport * as styles from \"./fullscreen-image-carousel.styles\";\nimport { StatefulImage } from \"./stateful-image\";\nimport type {\n FullscreenImageCarouselCustomItemProps,\n FullscreenImageCarouselItemProps,\n FullscreenImageCarouselProps,\n FullscreenImageCarouselRef,\n ImageDimension,\n} from \"./types\";\n\nconst isCustomItem = (\n item: FullscreenImageCarouselItemProps | undefined\n): item is FullscreenImageCarouselCustomItemProps =>\n !!item && item.type === \"custom\";\n\nexport const Component = (\n {\n items,\n initialActiveItemIndex,\n hideThumbnail = false,\n hideNavigation = false,\n hideCounter = false,\n hideMagnifier = false,\n onDelete,\n onClose,\n insets,\n show,\n ...otherProps\n }: FullscreenImageCarouselProps,\n ref: React.Ref<FullscreenImageCarouselRef>\n) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const [currentSlide, setCurrentSlide] = useStateCallback(\n initialActiveItemIndex ?? 0\n );\n const [imagesDimension, setImageDimension] = useState<\n Record<string, ImageDimension>\n >({});\n const [zoom, setZoom] = useState(1);\n const [startX, setStartX] = useState<number | undefined>();\n const [endX, setEndX] = useState<number | undefined>();\n const containerRef = useRef<HTMLDivElement>(null);\n const thumbnailRefs = useRef<(HTMLDivElement | null)[]>([]);\n const zoomRefs = useRef<(ReactZoomPanPinchContentRef | null)[]>([]);\n const imageRef = useRef<HTMLDivElement>(null);\n const topActionButtonsRef = useRef<HTMLDivElement>(null);\n const prevArrowButtonRef = useRef<HTMLButtonElement>(null);\n const nextArrowButtonRef = useRef<HTMLButtonElement>(null);\n const thumbnailContainerRef = useRef<HTMLDivElement>(null);\n const diff = startX && endX ? startX - endX : 0;\n const currentItem = items[currentSlide];\n const hasAnyItemLabel = items.some(\n (item) => isCustomItem(item) && !!item.itemLabel?.trim()\n );\n const carouselItemNoun = hasAnyItemLabel ? \"item\" : \"image\";\n const hasFileInfo = useMemo(\n () =>\n items.some(\n (item) => item.fileName?.trim() || item.fileSize?.trim()\n ),\n [items]\n );\n\n useApplyStyle(topActionButtonsRef, {\n [styles.tokens.topActionButtons.insetTop]: formatUnitValue(\n insets?.top,\n \"px\"\n ),\n [styles.tokens.topActionButtons.insetLeft]: formatUnitValue(\n insets?.left,\n \"px\"\n ),\n [styles.tokens.topActionButtons.insetRight]: formatUnitValue(\n insets?.right,\n \"px\"\n ),\n });\n\n useApplyStyle(prevArrowButtonRef, {\n [styles.tokens.arrowButton.insetLeft]: formatUnitValue(\n insets?.left,\n \"px\"\n ),\n });\n\n useApplyStyle(nextArrowButtonRef, {\n [styles.tokens.arrowButton.insetRight]: formatUnitValue(\n insets?.right,\n \"px\"\n ),\n });\n\n useApplyStyle(thumbnailContainerRef, {\n [styles.tokens.thumbnailContainer.insetBottom]: formatUnitValue(\n insets?.bottom,\n \"px\"\n ),\n });\n\n const getItemAriaLabel = useCallback(\n (index: number) => {\n const item = items[index];\n const itemTypeLabel = hasAnyItemLabel ? \"Item\" : \"Image\";\n const prefix = isCustomItem(item)\n ? item.itemLabel?.trim() || \"\"\n : item.alt?.trim() || \"\";\n const positionLabel = `${itemTypeLabel} ${index + 1} of ${\n items.length\n }.`;\n\n return prefix ? `${prefix}. ${positionLabel}` : positionLabel;\n },\n [items, hasAnyItemLabel]\n );\n\n useImperativeHandle<FullscreenImageCarouselRef, FullscreenImageCarouselRef>(\n ref,\n () => ({\n currentItemIndex: currentSlide,\n setCurrentItem: goToSlide,\n goToPrevItem: goToPrevSlide,\n goToNextItem: goToNextSlide,\n })\n );\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n useEventListener(\"keydown\", handleKeyDown, \"document\");\n\n useEffect(() => {\n if (show) {\n imageRef.current?.focus();\n }\n }, [show]);\n\n useEffect(() => {\n if (items.length === 0) {\n setCurrentSlide(0);\n return;\n }\n\n if (currentSlide > items.length - 1) {\n setCurrentSlide(items.length - 1);\n }\n }, [currentSlide, items.length, setCurrentSlide]);\n\n useEffect(() => {\n thumbnailRefs.current?.[currentSlide]?.scrollIntoView({\n behavior: \"smooth\",\n inline: \"center\",\n });\n setZoom(1);\n }, [currentSlide]);\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handleTouchStart = (e: React.TouchEvent) => {\n if (zoom <= 1) setStartX(e.touches[0].clientX);\n };\n\n const handleTouchMove = (e: React.TouchEvent) => {\n if (!startX || zoom > 1) return;\n setEndX(e.touches[0].clientX);\n };\n\n const handleTouchEnd = () => {\n if (!containerRef.current) return;\n if (Math.abs(diff) > containerRef.current.offsetWidth / 3) {\n if (diff > 0) {\n goToNextSlide();\n } else {\n goToPrevSlide();\n }\n }\n setStartX(undefined);\n setEndX(undefined);\n };\n\n const handleZoom = (e: ReactZoomPanPinchRef) => {\n setZoom(e.state.scale);\n };\n\n const handleDelete = () => {\n if (currentItem && onDelete) {\n onDelete(currentItem, currentSlide);\n }\n };\n\n function handleKeyDown(e: KeyboardEvent) {\n if (e.key === \"ArrowRight\") {\n goToNextSlide();\n } else if (e.key === \"ArrowLeft\") {\n goToPrevSlide();\n } else if (e.key === \"Escape\") {\n onClose?.();\n }\n }\n\n const handleMagnifier = () => {\n if (zoom === 1) {\n const zoomRatio = getZoomRatio();\n zoomRefs.current?.[currentSlide]?.centerView(zoomRatio);\n setZoom(zoomRatio ?? 1);\n } else {\n setZoom(1);\n zoomRefs.current?.[currentSlide]?.resetTransform();\n }\n };\n\n const setDimension = ({\n src,\n height,\n width,\n }: {\n src: string;\n height: number;\n width: number;\n }) => {\n setImageDimension((oldState) => {\n return { ...oldState, [src]: { height, width } };\n });\n };\n\n const getZoomRatio = () => {\n if (!currentItem || isCustomItem(currentItem)) {\n return;\n }\n\n const imageDimension = imagesDimension[currentItem.src];\n\n if (containerRef.current && !!imageDimension) {\n const { clientHeight, clientWidth } = containerRef.current;\n const { width, height } = imageDimension;\n const isSmallImg = width < clientWidth && height < clientHeight;\n\n const isImgLandscapeRelativeToDevice =\n height / width < clientHeight / clientWidth;\n\n if (isSmallImg) {\n return isImgLandscapeRelativeToDevice\n ? clientWidth / width\n : clientHeight / height;\n }\n\n return isImgLandscapeRelativeToDevice\n ? clientHeight / (height / (width / clientWidth))\n : clientWidth / (width / (height / clientHeight));\n }\n };\n\n // =============================================================================\n // HELPER FUNCTIONS\n // =============================================================================\n const goToPrevSlide = () => {\n zoomRefs.current?.[currentSlide]?.resetTransform();\n setCurrentSlide(\n (prev) => (prev === 0 ? items.length - 1 : prev - 1),\n (slide) => {\n clearAnnouncer(\"polite\");\n announce(getItemAriaLabel(slide), \"polite\");\n }\n );\n };\n\n const goToNextSlide = () => {\n zoomRefs.current?.[currentSlide]?.resetTransform();\n setCurrentSlide(\n (prev) => (prev === items.length - 1 ? 0 : prev + 1),\n (slide) => {\n clearAnnouncer(\"polite\");\n announce(getItemAriaLabel(slide), \"polite\");\n }\n );\n };\n\n const goToSlide = (index: number) => {\n zoomRefs.current?.[currentSlide]?.resetTransform();\n setCurrentSlide(index);\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n const renderSlides = () => {\n return (\n <div\n className={styles.imageGallerySlides}\n style={{\n transform: `translateX(calc(${\n -currentSlide * 100\n }% - ${diff}px))`,\n }}\n >\n {items.map((item, index) => {\n const isActive = index === currentSlide;\n const isActiveOrAdjacent =\n Math.abs(index - currentSlide) <= 1 ||\n (currentSlide === 0 && index === items.length - 1) ||\n (currentSlide === items.length - 1 && index === 0);\n\n return (\n <div\n className={styles.imageGallerySlide}\n key={index}\n data-testid=\"slide-item\"\n >\n <div\n className={styles.focusableImageRegion}\n ref={isActive ? imageRef : null}\n tabIndex={isActive ? 0 : -1}\n >\n {isCustomItem(item) ? (\n isActiveOrAdjacent ? (\n item.renderContent()\n ) : (\n <ImagePlaceholder\n className={\n styles.slidePlaceholderImage\n }\n />\n )\n ) : (\n <TransformWrapper\n ref={(el) =>\n (zoomRefs.current[index] = el)\n }\n panning={{\n disabled: zoom <= 1,\n }}\n initialScale={1}\n onZoom={handleZoom}\n onZoomStop={handleZoom}\n onWheel={handleZoom}\n >\n <TransformComponent>\n <StatefulImage\n className={styles.slideImage}\n src={item.src}\n alt={getItemAriaLabel(index)}\n placeholder={\n <ImagePlaceholder\n className={\n styles.slidePlaceholderImage\n }\n />\n }\n fit=\"scale-down\"\n retrieveImageDimension\n setDimension={setDimension}\n />\n </TransformComponent>\n </TransformWrapper>\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n };\n\n const renderFileInfo = () => {\n const { fileName, fileSize } = currentItem ?? {};\n const trimmedName = fileName?.trim();\n const trimmedSize = fileSize?.trim();\n\n return (\n <div\n className={clsx(\n styles.fileInfoTextWrapper,\n !trimmedSize && styles.fileInfoTextWrapperCentered\n )}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n data-testid=\"file-info-bar\"\n >\n {trimmedName && (\n <Typography.BodyBL\n className={styles.fileInfoFileName}\n weight=\"semibold\"\n data-testid=\"file-info-name\"\n >\n {trimmedName}\n </Typography.BodyBL>\n )}\n {trimmedSize && (\n <Typography.BodyMD\n className={styles.fileInfoFileSize}\n data-testid=\"file-info-size\"\n >\n {trimmedSize}\n </Typography.BodyMD>\n )}\n </div>\n );\n };\n\n const renderThumbnails = () => {\n return (\n <div\n className={styles.thumbnailContainer}\n ref={thumbnailContainerRef}\n aria-hidden=\"true\"\n data-testid=\"thumbnail-container\"\n >\n <div className={styles.thumbnailWrapper}>\n {items.map((item, index) => {\n const src = isCustomItem(item)\n ? item.thumbnailSrc\n : item.thumbnailSrc ?? item.src;\n return (\n <div\n className={styles.thumbnailItemContainer}\n key={index}\n >\n <div\n className={styles.thumbnailItem}\n data-active={index === currentSlide}\n data-testid=\"thumbnail-item\"\n onClick={() => goToSlide(index)}\n ref={(el) =>\n (thumbnailRefs.current[index] = el)\n }\n >\n {src ? (\n <StatefulImage\n className={styles.thumbnailImage}\n src={src}\n alt={`Thumbnail ${index + 1}`}\n fit=\"cover\"\n />\n ) : (\n <ImagePlaceholder\n className={\n styles.slidePlaceholderImage\n }\n />\n )}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n };\n\n return (\n <ModalV2\n {...otherProps}\n data-testid=\"image-carousel-modal\"\n aria-label={hasAnyItemLabel ? \"Carousel\" : \"Image carousel\"}\n show={show}\n disableInitialFocus\n >\n <div className={styles.carouselModalContent}>\n <div className={styles.imageGalleryContainer}>\n <div className={styles.imageGalleryWrapper}>\n <div\n className={styles.imageGallerySwipe}\n ref={containerRef}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n {renderSlides()}\n </div>\n\n {!hideNavigation && (\n <>\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.arrowButton,\n styles.arrowButtonLeft\n )}\n ref={prevArrowButtonRef}\n aria-label={`Previous ${carouselItemNoun}`}\n data-testid=\"prev-btn\"\n onClick={goToPrevSlide}\n >\n <ChevronLeftIcon aria-hidden />\n </ClickableIcon>\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.arrowButton,\n styles.arrowButtonRight\n )}\n ref={nextArrowButtonRef}\n aria-label={`Next ${carouselItemNoun}`}\n data-testid=\"forward-btn\"\n onClick={goToNextSlide}\n >\n <ChevronRightIcon aria-hidden />\n </ClickableIcon>\n </>\n )}\n\n {!hideCounter && (\n <div className={styles.boxChip} aria-hidden=\"true\">\n <Typography.BodyXS\n className={styles.chip}\n weight=\"semibold\"\n data-testid=\"carousel-counter\"\n >{`${currentSlide + 1}/${\n items.length\n }`}</Typography.BodyXS>\n </div>\n )}\n </div>\n\n {!hideThumbnail && renderThumbnails()}\n </div>\n <div\n className={styles.topActionButtons}\n ref={topActionButtonsRef}\n data-has-file-info={hasFileInfo}\n >\n {hasFileInfo && renderFileInfo()}\n {!hideMagnifier && !isCustomItem(currentItem) && (\n <ClickableIcon\n className={styles.iconButton}\n aria-label={zoom === 1 ? \"Zoom in\" : \"Zoom out\"}\n onClick={handleMagnifier}\n >\n {zoom === 1 ? (\n <MagnifierPlusIcon aria-hidden />\n ) : (\n <MagnifierMinusIcon aria-hidden />\n )}\n </ClickableIcon>\n )}\n\n {onDelete && (\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.iconButtonError\n )}\n aria-label={`Delete ${\n (isCustomItem(currentItem) &&\n currentItem.itemLabel?.trim()) ||\n \"image\"\n }`}\n data-testid=\"delete-btn\"\n onClick={handleDelete}\n >\n <BinIcon aria-hidden />\n </ClickableIcon>\n )}\n\n <ClickableIcon\n className={styles.iconButton}\n aria-label={\n hasAnyItemLabel\n ? \"Close carousel\"\n : \"Close image carousel\"\n }\n onClick={onClose}\n >\n <CrossIcon aria-hidden />\n </ClickableIcon>\n </div>\n </div>\n </ModalV2>\n );\n};\n\n// @catalog\n/**\n * A fullscreen modal carousel for browsing images and custom media items.\n *\n * Use to present a collection of images or custom content in an overlay with\n * navigation controls, zoom support, and an optional thumbnail strip.\n * The top info bar is rendered only when at least one item provides some file metadata.\n * @keywords carousel, gallery, image, lightbox, media, overlay\n */\nexport const FullscreenImageCarousel = forwardRef<\n FullscreenImageCarouselRef,\n FullscreenImageCarouselProps\n>(Component);\n"],"names":["isCustomItem","item","type","Component","_a","ref","items","initialActiveItemIndex","hideThumbnail","hideNavigation","hideCounter","hideMagnifier","onDelete","onClose","insets","show","otherProps","__rest","currentSlide","setCurrentSlide","useStateCallback","imagesDimension","setImageDimension","useState","zoom","setZoom","startX","setStartX","endX","setEndX","containerRef","useRef","thumbnailRefs","zoomRefs","imageRef","topActionButtonsRef","prevArrowButtonRef","nextArrowButtonRef","thumbnailContainerRef","diff","currentItem","hasAnyItemLabel","some","itemLabel","trim","carouselItemNoun","hasFileInfo","useMemo","fileName","fileSize","_b","useApplyStyle","styles.tokens","topActionButtons","insetTop","formatUnitValue","top","insetLeft","left","insetRight","right","arrowButton","thumbnailContainer","insetBottom","bottom","getItemAriaLabel","useCallback","index","itemTypeLabel","prefix","alt","positionLabel","length","useImperativeHandle","currentItemIndex","setCurrentItem","goToSlide","goToPrevItem","goToPrevSlide","goToNextItem","goToNextSlide","useEventListener","e","key","useEffect","current","focus","scrollIntoView","behavior","inline","handleZoom","state","scale","setDimension","src","height","width","oldState","Object","assign","getZoomRatio","imageDimension","clientHeight","clientWidth","isImgLandscapeRelativeToDevice","resetTransform","prev","slide","clearAnnouncer","announce","_jsx","ModalV2","disableInitialFocus","_jsxs","className","styles.carouselModalContent","children","styles.imageGalleryContainer","styles.imageGalleryWrapper","styles.imageGallerySwipe","onTouchStart","touches","clientX","onTouchMove","onTouchEnd","Math","abs","offsetWidth","undefined","styles.imageGallerySlides","style","transform","map","isActive","isActiveOrAdjacent","styles.imageGallerySlide","styles.focusableImageRegion","tabIndex","renderContent","ImagePlaceholder","styles.slidePlaceholderImage","TransformWrapper","el","panning","disabled","initialScale","onZoom","onZoomStop","onWheel","TransformComponent","StatefulImage","styles.slideImage","placeholder","fit","retrieveImageDimension","_Fragment","ClickableIcon","clsx","styles.iconButton","styles.arrowButton","styles.arrowButtonLeft","onClick","ChevronLeftIcon","styles.arrowButtonRight","ChevronRightIcon","styles.boxChip","Typography","BodyXS","styles.chip","weight","styles.thumbnailContainer","styles.thumbnailWrapper","thumbnailSrc","styles.thumbnailItemContainer","styles.thumbnailItem","styles.thumbnailImage","styles.topActionButtons","trimmedName","trimmedSize","styles.fileInfoTextWrapper","styles.fileInfoTextWrapperCentered","BodyBL","styles.fileInfoFileName","BodyMD","styles.fileInfoFileSize","renderFileInfo","zoomRatio","centerView","_d","_c","MagnifierPlusIcon","MagnifierMinusIcon","styles.iconButtonError","BinIcon","CrossIcon","FullscreenImageCarousel","forwardRef"],"mappings":"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"}
1
+ {"version":3,"file":"fullscreen-image-carousel.js","sources":["../../src/fullscreen-image-carousel/fullscreen-image-carousel.tsx"],"sourcesContent":["import {\n ChevronLeftIcon,\n ChevronRightIcon,\n CrossIcon,\n MagnifierMinusIcon,\n MagnifierPlusIcon,\n} from \"@lifesg/react-icons\";\nimport { BinIcon } from \"@lifesg/react-icons/bin\";\nimport { announce, clearAnnouncer } from \"@react-aria/live-announcer\";\nimport clsx from \"clsx\";\nimport {\n cloneElement,\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type {\n ReactZoomPanPinchContentRef,\n ReactZoomPanPinchRef,\n} from \"react-zoom-pan-pinch\";\nimport { TransformComponent, TransformWrapper } from \"react-zoom-pan-pinch\";\n\nimport { ModalV2 } from \"../modal-v2\";\nimport { ClickableIcon } from \"../shared/clickable-icon\";\nimport { useStateCallback } from \"../shared/hooks\";\nimport { ImagePlaceholder } from \"../shared/image-placeholder\";\nimport { formatUnitValue, useApplyStyle } from \"../theme\";\nimport { Typography } from \"../typography\";\nimport { mergeRefs, useEventListener } from \"../util\";\nimport * as styles from \"./fullscreen-image-carousel.styles\";\nimport { StatefulImage } from \"./stateful-image\";\nimport type {\n FullscreenImageCarouselCustomAction,\n FullscreenImageCarouselCustomItemProps,\n FullscreenImageCarouselItemProps,\n FullscreenImageCarouselProps,\n FullscreenImageCarouselRef,\n ImageDimension,\n} from \"./types\";\n\nconst isCustomItem = (\n item: FullscreenImageCarouselItemProps | undefined\n): item is FullscreenImageCarouselCustomItemProps =>\n !!item && item.type === \"custom\";\n\nexport const Component = (\n {\n items,\n initialActiveItemIndex,\n hideThumbnail = false,\n hideNavigation = false,\n hideCounter = false,\n hideMagnifier = false,\n customActions,\n onDelete,\n onClose,\n insets,\n topBarRef,\n show,\n ...otherProps\n }: FullscreenImageCarouselProps,\n ref: React.Ref<FullscreenImageCarouselRef>\n) => {\n // =============================================================================\n // CONST, STATE, REF\n // =============================================================================\n const [currentSlide, setCurrentSlide] = useStateCallback(\n initialActiveItemIndex ?? 0\n );\n const [imagesDimension, setImageDimension] = useState<\n Record<string, ImageDimension>\n >({});\n const [zoom, setZoom] = useState(1);\n const [startX, setStartX] = useState<number | undefined>();\n const [endX, setEndX] = useState<number | undefined>();\n const containerRef = useRef<HTMLDivElement>(null);\n const thumbnailRefs = useRef<(HTMLDivElement | null)[]>([]);\n const zoomRefs = useRef<(ReactZoomPanPinchContentRef | null)[]>([]);\n const imageRef = useRef<HTMLDivElement>(null);\n /*\n * The top bar's element is held in state rather than a ref so that its mount\n * causes a render. Overlay keeps its portal target in state and mounts these\n * children, so the layout effect below would otherwise run while a ref was\n * still null and never write the inset custom properties.\n */\n const [topActionButtons, setTopActionButtons] =\n useState<HTMLDivElement | null>(null);\n const topActionButtonsRef = useMemo(\n () => ({ current: topActionButtons }),\n [topActionButtons]\n );\n const mergedTopBarRef = useMemo(\n () => mergeRefs(setTopActionButtons, topBarRef),\n [topBarRef]\n );\n const prevArrowButtonRef = useRef<HTMLButtonElement>(null);\n const nextArrowButtonRef = useRef<HTMLButtonElement>(null);\n const thumbnailContainerRef = useRef<HTMLDivElement>(null);\n const diff = startX && endX ? startX - endX : 0;\n const currentItem = items[currentSlide];\n const resolvedCustomActions =\n currentItem?.customActions ?? customActions ?? [];\n const hasAnyItemLabel = items.some(\n (item) => isCustomItem(item) && !!item.itemLabel?.trim()\n );\n const carouselItemNoun = hasAnyItemLabel ? \"item\" : \"image\";\n const hasFileInfo = useMemo(\n () =>\n items.some(\n (item) => item.fileName?.trim() || item.fileSize?.trim()\n ),\n [items]\n );\n\n useApplyStyle(topActionButtonsRef, {\n [styles.tokens.topActionButtons.insetTop]: formatUnitValue(\n insets?.top,\n \"px\"\n ),\n [styles.tokens.topActionButtons.insetLeft]: formatUnitValue(\n insets?.left,\n \"px\"\n ),\n [styles.tokens.topActionButtons.insetRight]: formatUnitValue(\n insets?.right,\n \"px\"\n ),\n });\n\n useApplyStyle(prevArrowButtonRef, {\n [styles.tokens.arrowButton.insetLeft]: formatUnitValue(\n insets?.left,\n \"px\"\n ),\n });\n\n useApplyStyle(nextArrowButtonRef, {\n [styles.tokens.arrowButton.insetRight]: formatUnitValue(\n insets?.right,\n \"px\"\n ),\n });\n\n useApplyStyle(thumbnailContainerRef, {\n [styles.tokens.thumbnailContainer.insetBottom]: formatUnitValue(\n insets?.bottom,\n \"px\"\n ),\n });\n\n const getItemAriaLabel = useCallback(\n (index: number) => {\n const item = items[index];\n const itemTypeLabel = hasAnyItemLabel ? \"Item\" : \"Image\";\n const prefix = isCustomItem(item)\n ? item.itemLabel?.trim() || \"\"\n : item.alt?.trim() || \"\";\n const positionLabel = `${itemTypeLabel} ${index + 1} of ${\n items.length\n }.`;\n\n return prefix ? `${prefix}. ${positionLabel}` : positionLabel;\n },\n [items, hasAnyItemLabel]\n );\n\n useImperativeHandle<FullscreenImageCarouselRef, FullscreenImageCarouselRef>(\n ref,\n () => ({\n currentItemIndex: currentSlide,\n setCurrentItem: goToSlide,\n goToPrevItem: goToPrevSlide,\n goToNextItem: goToNextSlide,\n })\n );\n\n // =============================================================================\n // EFFECTS\n // =============================================================================\n useEventListener(\"keydown\", handleKeyDown, \"document\");\n\n useEffect(() => {\n if (show) {\n imageRef.current?.focus();\n }\n }, [show]);\n\n useEffect(() => {\n if (items.length === 0) {\n setCurrentSlide(0);\n return;\n }\n\n if (currentSlide > items.length - 1) {\n setCurrentSlide(items.length - 1);\n }\n }, [currentSlide, items.length, setCurrentSlide]);\n\n useEffect(() => {\n thumbnailRefs.current?.[currentSlide]?.scrollIntoView({\n behavior: \"smooth\",\n inline: \"center\",\n });\n setZoom(1);\n }, [currentSlide]);\n\n // =============================================================================\n // EVENT HANDLERS\n // =============================================================================\n const handleTouchStart = (e: React.TouchEvent) => {\n if (zoom <= 1) setStartX(e.touches[0].clientX);\n };\n\n const handleTouchMove = (e: React.TouchEvent) => {\n if (!startX || zoom > 1) return;\n setEndX(e.touches[0].clientX);\n };\n\n const handleTouchEnd = () => {\n if (!containerRef.current) return;\n if (Math.abs(diff) > containerRef.current.offsetWidth / 3) {\n if (diff > 0) {\n goToNextSlide();\n } else {\n goToPrevSlide();\n }\n }\n setStartX(undefined);\n setEndX(undefined);\n };\n\n const handleZoom = (e: ReactZoomPanPinchRef) => {\n setZoom(e.state.scale);\n };\n\n const handleDelete = () => {\n if (currentItem && onDelete) {\n onDelete(currentItem, currentSlide);\n }\n };\n\n const handleCustomAction = (\n action: FullscreenImageCarouselCustomAction\n ) => {\n if (currentItem) {\n action.onClick(currentItem, currentSlide);\n }\n };\n\n function handleKeyDown(e: KeyboardEvent) {\n if (e.key === \"ArrowRight\") {\n goToNextSlide();\n } else if (e.key === \"ArrowLeft\") {\n goToPrevSlide();\n } else if (e.key === \"Escape\") {\n onClose?.();\n }\n }\n\n const handleMagnifier = () => {\n if (zoom === 1) {\n const zoomRatio = getZoomRatio();\n zoomRefs.current?.[currentSlide]?.centerView(zoomRatio);\n setZoom(zoomRatio ?? 1);\n } else {\n setZoom(1);\n zoomRefs.current?.[currentSlide]?.resetTransform();\n }\n };\n\n const setDimension = ({\n src,\n height,\n width,\n }: {\n src: string;\n height: number;\n width: number;\n }) => {\n setImageDimension((oldState) => {\n return { ...oldState, [src]: { height, width } };\n });\n };\n\n const getZoomRatio = () => {\n if (!currentItem || isCustomItem(currentItem)) {\n return;\n }\n\n const imageDimension = imagesDimension[currentItem.src];\n\n if (containerRef.current && !!imageDimension) {\n const { clientHeight, clientWidth } = containerRef.current;\n const { width, height } = imageDimension;\n const isSmallImg = width < clientWidth && height < clientHeight;\n\n const isImgLandscapeRelativeToDevice =\n height / width < clientHeight / clientWidth;\n\n if (isSmallImg) {\n return isImgLandscapeRelativeToDevice\n ? clientWidth / width\n : clientHeight / height;\n }\n\n return isImgLandscapeRelativeToDevice\n ? clientHeight / (height / (width / clientWidth))\n : clientWidth / (width / (height / clientHeight));\n }\n };\n\n // =============================================================================\n // HELPER FUNCTIONS\n // =============================================================================\n const goToPrevSlide = () => {\n zoomRefs.current?.[currentSlide]?.resetTransform();\n setCurrentSlide(\n (prev) => (prev === 0 ? items.length - 1 : prev - 1),\n (slide) => {\n clearAnnouncer(\"polite\");\n announce(getItemAriaLabel(slide), \"polite\");\n }\n );\n };\n\n const goToNextSlide = () => {\n zoomRefs.current?.[currentSlide]?.resetTransform();\n setCurrentSlide(\n (prev) => (prev === items.length - 1 ? 0 : prev + 1),\n (slide) => {\n clearAnnouncer(\"polite\");\n announce(getItemAriaLabel(slide), \"polite\");\n }\n );\n };\n\n const goToSlide = (index: number) => {\n zoomRefs.current?.[currentSlide]?.resetTransform();\n setCurrentSlide(index);\n };\n\n // =============================================================================\n // RENDER FUNCTIONS\n // =============================================================================\n const renderSlides = () => {\n return (\n <div\n className={styles.imageGallerySlides}\n style={{\n transform: `translateX(calc(${\n -currentSlide * 100\n }% - ${diff}px))`,\n }}\n >\n {items.map((item, index) => {\n const isActive = index === currentSlide;\n const isActiveOrAdjacent =\n Math.abs(index - currentSlide) <= 1 ||\n (currentSlide === 0 && index === items.length - 1) ||\n (currentSlide === items.length - 1 && index === 0);\n\n return (\n <div\n className={styles.imageGallerySlide}\n key={index}\n data-testid=\"slide-item\"\n >\n <div\n className={styles.focusableImageRegion}\n ref={isActive ? imageRef : null}\n tabIndex={isActive ? 0 : -1}\n >\n {isCustomItem(item) ? (\n isActiveOrAdjacent ? (\n item.renderContent()\n ) : (\n <ImagePlaceholder\n className={\n styles.slidePlaceholderImage\n }\n />\n )\n ) : (\n <TransformWrapper\n ref={(el) =>\n (zoomRefs.current[index] = el)\n }\n panning={{\n disabled: zoom <= 1,\n }}\n initialScale={1}\n onZoom={handleZoom}\n onZoomStop={handleZoom}\n onWheel={handleZoom}\n >\n <TransformComponent>\n <StatefulImage\n className={styles.slideImage}\n src={item.src}\n alt={getItemAriaLabel(index)}\n placeholder={\n <ImagePlaceholder\n className={\n styles.slidePlaceholderImage\n }\n />\n }\n fit=\"scale-down\"\n retrieveImageDimension\n setDimension={setDimension}\n />\n </TransformComponent>\n </TransformWrapper>\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n };\n\n const renderFileInfo = () => {\n const { fileName, fileSize } = currentItem ?? {};\n const trimmedName = fileName?.trim();\n const trimmedSize = fileSize?.trim();\n\n return (\n <div\n className={clsx(\n styles.fileInfoTextWrapper,\n !trimmedSize && styles.fileInfoTextWrapperCentered\n )}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n data-testid=\"file-info-bar\"\n >\n {trimmedName && (\n <Typography.BodyBL\n className={styles.fileInfoFileName}\n weight=\"semibold\"\n data-testid=\"file-info-name\"\n >\n {trimmedName}\n </Typography.BodyBL>\n )}\n {trimmedSize && (\n <Typography.BodyMD\n className={styles.fileInfoFileSize}\n data-testid=\"file-info-size\"\n >\n {trimmedSize}\n </Typography.BodyMD>\n )}\n </div>\n );\n };\n\n const renderThumbnails = () => {\n return (\n <div\n className={styles.thumbnailContainer}\n ref={thumbnailContainerRef}\n aria-hidden=\"true\"\n data-testid=\"thumbnail-container\"\n >\n <div className={styles.thumbnailWrapper}>\n {items.map((item, index) => {\n const src = isCustomItem(item)\n ? item.thumbnailSrc\n : item.thumbnailSrc ?? item.src;\n return (\n <div\n className={styles.thumbnailItemContainer}\n key={index}\n >\n <div\n className={styles.thumbnailItem}\n data-active={index === currentSlide}\n data-testid=\"thumbnail-item\"\n onClick={() => goToSlide(index)}\n ref={(el) =>\n (thumbnailRefs.current[index] = el)\n }\n >\n {src ? (\n <StatefulImage\n className={styles.thumbnailImage}\n src={src}\n alt={`Thumbnail ${index + 1}`}\n fit=\"cover\"\n />\n ) : (\n <ImagePlaceholder\n className={\n styles.slidePlaceholderImage\n }\n />\n )}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n };\n\n const renderTopActions = () => {\n return (\n <div\n className={styles.topActionButtons}\n ref={mergedTopBarRef}\n data-has-file-info={hasFileInfo}\n >\n {hasFileInfo && renderFileInfo()}\n\n {resolvedCustomActions.map((action, index) => (\n <ClickableIcon\n key={index}\n className={styles.iconButton}\n aria-label={action.ariaLabel}\n data-testid={action[\"data-testid\"]}\n onClick={() => handleCustomAction(action)}\n >\n {cloneElement(action.icon, { \"aria-hidden\": true })}\n </ClickableIcon>\n ))}\n\n {!hideMagnifier && !isCustomItem(currentItem) && (\n <ClickableIcon\n className={styles.iconButton}\n aria-label={zoom === 1 ? \"Zoom in\" : \"Zoom out\"}\n onClick={handleMagnifier}\n >\n {zoom === 1 ? (\n <MagnifierPlusIcon aria-hidden />\n ) : (\n <MagnifierMinusIcon aria-hidden />\n )}\n </ClickableIcon>\n )}\n\n {onDelete && (\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.iconButtonError\n )}\n aria-label={`Delete ${\n (isCustomItem(currentItem) &&\n currentItem.itemLabel?.trim()) ||\n \"image\"\n }`}\n data-testid=\"delete-btn\"\n onClick={handleDelete}\n >\n <BinIcon aria-hidden />\n </ClickableIcon>\n )}\n\n <ClickableIcon\n className={styles.iconButton}\n aria-label={\n hasAnyItemLabel\n ? \"Close carousel\"\n : \"Close image carousel\"\n }\n onClick={onClose}\n >\n <CrossIcon aria-hidden />\n </ClickableIcon>\n </div>\n );\n };\n\n return (\n <ModalV2\n {...otherProps}\n data-testid=\"image-carousel-modal\"\n aria-label={hasAnyItemLabel ? \"Carousel\" : \"Image carousel\"}\n show={show}\n disableInitialFocus\n >\n <div className={styles.carouselModalContent}>\n <div className={styles.imageGalleryContainer}>\n <div className={styles.imageGalleryWrapper}>\n <div\n className={styles.imageGallerySwipe}\n ref={containerRef}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n {renderSlides()}\n </div>\n\n {!hideNavigation && (\n <>\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.arrowButton,\n styles.arrowButtonLeft\n )}\n ref={prevArrowButtonRef}\n aria-label={`Previous ${carouselItemNoun}`}\n data-testid=\"prev-btn\"\n onClick={goToPrevSlide}\n >\n <ChevronLeftIcon aria-hidden />\n </ClickableIcon>\n <ClickableIcon\n className={clsx(\n styles.iconButton,\n styles.arrowButton,\n styles.arrowButtonRight\n )}\n ref={nextArrowButtonRef}\n aria-label={`Next ${carouselItemNoun}`}\n data-testid=\"forward-btn\"\n onClick={goToNextSlide}\n >\n <ChevronRightIcon aria-hidden />\n </ClickableIcon>\n </>\n )}\n\n {!hideCounter && (\n <div className={styles.boxChip} aria-hidden=\"true\">\n <Typography.BodyXS\n className={styles.chip}\n weight=\"semibold\"\n data-testid=\"carousel-counter\"\n >{`${currentSlide + 1}/${\n items.length\n }`}</Typography.BodyXS>\n </div>\n )}\n </div>\n\n {!hideThumbnail && renderThumbnails()}\n </div>\n {renderTopActions()}\n </div>\n </ModalV2>\n );\n};\n\n// @catalog\n/**\n * A fullscreen modal carousel for browsing images and custom media items.\n *\n * Use to present a collection of images or custom content in an overlay with\n * navigation controls, zoom support, and an optional thumbnail strip.\n * The top info bar is rendered only when at least one item provides some file metadata.\n * @keywords carousel, gallery, image, lightbox, media, overlay\n */\nexport const FullscreenImageCarousel = forwardRef<\n FullscreenImageCarouselRef,\n FullscreenImageCarouselProps\n>(Component);\n"],"names":["isCustomItem","item","type","Component","_a","ref","items","initialActiveItemIndex","hideThumbnail","hideNavigation","hideCounter","hideMagnifier","customActions","onDelete","onClose","insets","topBarRef","show","otherProps","__rest","currentSlide","setCurrentSlide","useStateCallback","imagesDimension","setImageDimension","useState","zoom","setZoom","startX","setStartX","endX","setEndX","containerRef","useRef","thumbnailRefs","zoomRefs","imageRef","topActionButtons","setTopActionButtons","topActionButtonsRef","useMemo","current","mergedTopBarRef","mergeRefs","prevArrowButtonRef","nextArrowButtonRef","thumbnailContainerRef","diff","currentItem","resolvedCustomActions","_c","_b","hasAnyItemLabel","some","itemLabel","trim","carouselItemNoun","hasFileInfo","fileName","fileSize","useApplyStyle","styles.tokens","insetTop","formatUnitValue","top","insetLeft","left","insetRight","right","arrowButton","thumbnailContainer","insetBottom","bottom","getItemAriaLabel","useCallback","index","itemTypeLabel","prefix","alt","positionLabel","length","useImperativeHandle","currentItemIndex","setCurrentItem","goToSlide","goToPrevItem","goToPrevSlide","goToNextItem","goToNextSlide","useEventListener","e","key","useEffect","focus","scrollIntoView","behavior","inline","handleZoom","state","scale","handleDelete","handleMagnifier","zoomRatio","getZoomRatio","centerView","_d","resetTransform","setDimension","src","height","width","oldState","Object","assign","imageDimension","clientHeight","clientWidth","isImgLandscapeRelativeToDevice","prev","slide","clearAnnouncer","announce","renderFileInfo","trimmedName","trimmedSize","_jsxs","className","clsx","styles.fileInfoTextWrapper","styles.fileInfoTextWrapperCentered","children","_jsx","Typography","BodyBL","styles.fileInfoFileName","weight","BodyMD","styles.fileInfoFileSize","ModalV2","disableInitialFocus","styles.carouselModalContent","styles.imageGalleryContainer","styles.imageGalleryWrapper","styles.imageGallerySwipe","onTouchStart","touches","clientX","onTouchMove","onTouchEnd","Math","abs","offsetWidth","undefined","styles.imageGallerySlides","style","transform","map","isActive","isActiveOrAdjacent","styles.imageGallerySlide","styles.focusableImageRegion","tabIndex","renderContent","ImagePlaceholder","styles.slidePlaceholderImage","TransformWrapper","el","panning","disabled","initialScale","onZoom","onZoomStop","onWheel","TransformComponent","StatefulImage","styles.slideImage","placeholder","fit","retrieveImageDimension","_Fragment","ClickableIcon","styles.iconButton","styles.arrowButton","styles.arrowButtonLeft","onClick","ChevronLeftIcon","styles.arrowButtonRight","ChevronRightIcon","styles.boxChip","BodyXS","styles.chip","styles.thumbnailContainer","styles.thumbnailWrapper","thumbnailSrc","styles.thumbnailItemContainer","styles.thumbnailItem","styles.thumbnailImage","styles.topActionButtons","action","ariaLabel","handleCustomAction","cloneElement","icon","MagnifierPlusIcon","MagnifierMinusIcon","styles.iconButtonError","BinIcon","CrossIcon","renderTopActions","FullscreenImageCarousel","forwardRef"],"mappings":"qwEA4CA,MAAMA,GACFC,KAEEA,GAAsB,WAAdA,EAAKC,KAENC,GAAY,CACrBC,EAeAC,iBAfAC,MACIA,GAAKC,uBACLA,GAAsBC,cACtBA,IAAgB,EAAKC,eACrBA,IAAiB,EAAKC,YACtBA,IAAc,EAAKC,cACnBA,IAAgB,EAAKC,cACrBA,GAAaC,SACbA,GAAQC,QACRA,GAAOC,OACPA,GAAMC,UACNA,GAASC,KACTA,IAAIb,EACDc,GAAUC,EAAAf,EAbjB,oKAoBA,MAAOgB,GAAcC,IAAmBC,EACpCf,SAAAA,GAA0B,IAEvBgB,GAAiBC,IAAqBC,EAE3C,CAAA,IACKC,GAAMC,IAAWF,EAAS,IAC1BG,GAAQC,IAAaJ,KACrBK,GAAMC,IAAWN,IAClBO,GAAeC,EAAuB,MACtCC,GAAgBD,EAAkC,IAClDE,GAAWF,EAA+C,IAC1DG,GAAWH,EAAuB,OAOjCI,GAAkBC,IACrBb,EAAgC,MAC9Bc,GAAsBC,GACxB,MAASC,QAASJ,MAClB,CAACA,KAECK,GAAkBF,GACpB,IAAMG,EAAUL,GAAqBtB,KACrC,CAACA,KAEC4B,GAAqBX,EAA0B,MAC/CY,GAAqBZ,EAA0B,MAC/Ca,GAAwBb,EAAuB,MAC/Cc,GAAOnB,IAAUE,GAAOF,GAASE,GAAO,EACxCkB,GAAc1C,GAAMc,IACpB6B,GACyC,QAA3CC,GAA0B,QAA1BC,GAAAH,cAAW,EAAXA,GAAapC,qBAAa,IAAAuC,GAAAA,GAAIvC,UAAa,IAAAsC,GAAAA,GAAI,GAC7CE,GAAkB9C,GAAM+C,MACzBpD,IAAQ,IAAAG,EAAC,OAAAJ,GAAaC,OAAyB,QAAdG,EAAAH,EAAKqD,iBAAS,IAAAlD,OAAA,EAAAA,EAAEmD,OAAM,IAEtDC,GAAmBJ,GAAkB,OAAS,QAC9CK,GAAcjB,GAChB,IACIlC,GAAM+C,MACDpD,YAAS,OAAa,UAAbA,EAAKyD,gBAAQ,IAAAtD,OAAA,EAAAA,EAAEmD,UAAuB,UAAbtD,EAAK0D,gBAAQ,IAAAR,OAAA,EAAAA,EAAEI,OAAM,KAEhE,CAACjD,KAGLsD,EAAcrB,GAAqB,CAC/B,CAACsB,EAAcxB,iBAAiByB,UAAWC,EACvChD,cAAM,EAANA,GAAQiD,IACR,MAEJ,CAACH,EAAcxB,iBAAiB4B,WAAYF,EACxChD,cAAM,EAANA,GAAQmD,KACR,MAEJ,CAACL,EAAcxB,iBAAiB8B,YAAaJ,EACzChD,cAAM,EAANA,GAAQqD,MACR,QAIRR,EAAchB,GAAoB,CAC9B,CAACiB,EAAcQ,YAAYJ,WAAYF,EACnChD,cAAM,EAANA,GAAQmD,KACR,QAIRN,EAAcf,GAAoB,CAC9B,CAACgB,EAAcQ,YAAYF,YAAaJ,EACpChD,cAAM,EAANA,GAAQqD,MACR,QAIRR,EAAcd,GAAuB,CACjC,CAACe,EAAcS,mBAAmBC,aAAcR,EAC5ChD,cAAM,EAANA,GAAQyD,OACR,QAIR,MAAMC,GAAmBC,GACpBC,YACG,MAAM1E,EAAOK,GAAMqE,GACbC,EAAgBxB,GAAkB,OAAS,QAC3CyB,EAAS7E,GAAaC,IACR,QAAdG,EAAAH,EAAKqD,iBAAS,IAAAlD,OAAA,EAAAA,EAAEmD,SAAU,IAClB,QAARJ,EAAAlD,EAAK6E,WAAG,IAAA3B,OAAA,EAAAA,EAAEI,SAAU,GACpBwB,EAAgB,GAAGH,KAAiBD,EAAQ,QAC9CrE,GAAM0E,UAGV,OAAOH,EAAS,GAAGA,MAAWE,IAAkBA,CAAa,GAEjE,CAACzE,GAAO8C,KAGZ6B,EACI5E,IACA,KAAA,CACI6E,iBAAkB9D,GAClB+D,eAAgBC,GAChBC,aAAcC,GACdC,aAAcC,OAOtBC,EAAiB,WAsEjB,SAAuBC,GACL,eAAVA,EAAEC,IACFH,KACiB,cAAVE,EAAEC,IACTL,KACiB,WAAVI,EAAEC,MACT7E,UAAAA,KAER,GA9E2C,YAE3C8E,GAAU,WACF3E,KACgB,QAAhBb,EAAAgC,GAASK,eAAO,IAAArC,GAAAA,EAAEyF,QACtB,GACD,CAAC5E,KAEJ2E,GAAU,KACe,IAAjBtF,GAAM0E,OAKN5D,GAAed,GAAM0E,OAAS,GAC9B3D,GAAgBf,GAAM0E,OAAS,GAL/B3D,GAAgB,EAMpB,GACD,CAACD,GAAcd,GAAM0E,OAAQ3D,KAEhCuE,GAAU,aAC+B,QAArCzC,UAAA/C,EAAA8B,GAAcO,8BAAUrB,WAAa,IAAA+B,GAAAA,EAAE2C,eAAe,CAClDC,SAAU,SACVC,OAAQ,WAEZrE,GAAQ,EAAE,GACX,CAACP,KAKJ,MAsBM6E,GAAcP,IAChB/D,GAAQ+D,EAAEQ,MAAMC,MAAM,EAGpBC,GAAe,KACbpD,IAAenC,IACfA,GAASmC,GAAa5B,GAC1B,EAqBJ,MAAMiF,GAAkB,iBACpB,GAAa,IAAT3E,GAAY,CACZ,MAAM4E,EAAYC,KACc,QAAhCpD,EAAgB,QAAhB/C,EAAA+B,GAASM,eAAO,IAAArC,OAAA,EAAAA,EAAGgB,WAAa,IAAA+B,GAAAA,EAAEqD,WAAWF,GAC7C3E,GAAQ2E,QAAAA,EAAa,EACzB,MACI3E,GAAQ,GACwB,QAAhC8E,EAAgB,QAAhBvD,EAAAf,GAASM,eAAO,IAAAS,OAAA,EAAAA,EAAG9B,WAAa,IAAAqF,GAAAA,EAAEC,gBACtC,EAGEC,GAAe,EACjBC,MACAC,SACAC,YAMAtF,IAAmBuF,GACfC,OAAAC,OAAAD,OAAAC,OAAA,CAAA,EAAYF,GAAQ,CAAEH,CAACA,GAAM,CAAEC,SAAQC,YACzC,EAGAP,GAAe,KACjB,IAAKvD,IAAehD,GAAagD,IAC7B,OAGJ,MAAMkE,EAAiB3F,GAAgByB,GAAY4D,KAEnD,GAAI5E,GAAaS,SAAayE,EAAgB,CAC1C,MAAMC,aAAEA,EAAYC,YAAEA,GAAgBpF,GAAaS,SAC7CqE,MAAEA,EAAKD,OAAEA,GAAWK,EAGpBG,EACFR,EAASC,EAAQK,EAAeC,EAEpC,OALmBN,EAAQM,GAAeP,EAASM,EAMxCE,EACDD,EAAcN,EACdK,EAAeN,EAGlBQ,EACDF,GAAgBN,GAAUC,EAAQM,IAClCA,GAAeN,GAASD,EAASM,GAC3C,GAME7B,GAAgB,aACc,QAAhCnC,EAAgB,QAAhB/C,EAAA+B,GAASM,eAAO,IAAArC,OAAA,EAAAA,EAAGgB,WAAa,IAAA+B,GAAAA,EAAEuD,iBAClCrF,IACKiG,GAAmB,IAATA,EAAahH,GAAM0E,OAAS,EAAIsC,EAAO,IACjDC,IACGC,EAAe,UACfC,EAAShD,GAAiB8C,GAAQ,SAAS,GAElD,EAGC/B,GAAgB,aACc,QAAhCrC,EAAgB,QAAhB/C,EAAA+B,GAASM,eAAO,IAAArC,OAAA,EAAAA,EAAGgB,WAAa,IAAA+B,GAAAA,EAAEuD,iBAClCrF,IACKiG,GAAUA,IAAShH,GAAM0E,OAAS,EAAI,EAAIsC,EAAO,IACjDC,IACGC,EAAe,UACfC,EAAShD,GAAiB8C,GAAQ,SAAS,GAElD,EAGCnC,GAAaT,YACiB,QAAhCxB,EAAgB,QAAhB/C,EAAA+B,GAASM,eAAO,IAAArC,OAAA,EAAAA,EAAGgB,WAAa,IAAA+B,GAAAA,EAAEuD,iBAClCrF,GAAgBsD,EAAM,EAoFpB+C,GAAiB,KACnB,MAAMhE,SAAEA,EAAQC,SAAEA,GAAaX,SAAAA,GAAe,CAAA,EACxC2E,EAAcjE,eAAAA,EAAUH,OACxBqE,EAAcjE,eAAAA,EAAUJ,OAE9B,OACIsE,EAAA,MAAA,CACIC,UAAWC,EACPC,IACCJ,GAAeK,IACnB,YACS,SAAQ,cACN,OAAM,cACN,gBAAeC,SAAA,CAE1BP,GACGQ,EAACC,EAAWC,QACRP,UAAWQ,GACXC,OAAO,WAAU,cACL,iBAAgBL,SAE3BP,IAGRC,GACGO,EAACC,EAAWI,OAAM,CACdV,UAAWW,GAAuB,cACtB,0BAEXb,MAGP,EA0Hd,OACIO,EAACO,EAAO1B,OAAAC,OAAA,GACA/F,GAAU,CAAA,cACF,uBAAsB,aACtBkC,GAAkB,WAAa,iBAC3CnC,KAAMA,GACN0H,qBAAmB,EAAAT,SAEnBL,EAAA,MAAA,CAAKC,UAAWc,EAA2BV,SAAA,CACvCL,SAAKC,UAAWe,EAA4BX,SAAA,CACxCL,SAAKC,UAAWgB,EAA0BZ,SAAA,CACtCC,SACIL,UAAWiB,EACX1I,IAAK2B,GACLgH,aA7XEtD,IAClBhE,IAAQ,GAAGG,GAAU6D,EAAEuD,QAAQ,GAAGC,QAAQ,EA6X1BC,YA1XCzD,KAChB9D,IAAUF,GAAO,GACtBK,GAAQ2D,EAAEuD,QAAQ,GAAGC,QAAQ,EAyXTE,WAtXD,KACdpH,GAAaS,UACd4G,KAAKC,IAAIvG,IAAQf,GAAaS,QAAQ8G,YAAc,IAChDxG,GAAO,EACPyC,KAEAF,MAGRzD,QAAU2H,GACVzH,QAAQyH,GAAU,EA4W4BtB,SAtP1CC,SACIL,UAAW2B,EACXC,MAAO,CACHC,UAAW,mBACS,KAAfvI,SACE2B,UACVmF,SAEA5H,GAAMsJ,KAAI,CAAC3J,EAAM0E,KACd,MAAMkF,EAAWlF,IAAUvD,GACrB0I,EACFT,KAAKC,IAAI3E,EAAQvD,KAAiB,GAChB,IAAjBA,IAAsBuD,IAAUrE,GAAM0E,OAAS,GAC/C5D,KAAiBd,GAAM0E,OAAS,GAAe,IAAVL,EAE1C,OACIwD,SACIL,UAAWiC,EAAwB,cAEvB,aAAY7B,SAExBC,EAAA,MAAA,CACIL,UAAWkC,EACX3J,IAAKwJ,EAAWzH,GAAW,KAC3B6H,SAAUJ,EAAW,GAAI,EAAE3B,SAE1BlI,GAAaC,GACV6J,EACI7J,EAAKiK,gBAEL/B,EAACgC,GACGrC,UACIsC,IAKZjC,EAACkC,EAAgB,CACbhK,IAAMiK,GACDnI,GAASM,QAAQkC,GAAS2F,EAE/BC,QAAS,CACLC,SAAU9I,IAAQ,GAEtB+I,aAAc,EACdC,OAAQzE,GACR0E,WAAY1E,GACZ2E,QAAS3E,GAAUiC,SAEnBC,EAAC0C,EAAkB,CAAA3C,SACfC,EAAC2C,GAAa,CACVhD,UAAWiD,EACXnE,IAAK3G,EAAK2G,IACV9B,IAAKL,GAAiBE,GACtBqG,YACI7C,EAACgC,EAAgB,CACbrC,UACIsC,IAIZa,IAAI,aACJC,wBAAsB,EACtBvE,aAAcA,YA7C7BhC,EAmDH,SAsLJlE,IACEoH,EAAAsD,EAAA,CAAAjD,SAAA,CACIC,EAACiD,GACGtD,UAAWC,EACPsD,EACAC,EACAC,GAEJlL,IAAKuC,GAAkB,aACX,YAAYY,KAAkB,cAC9B,WACZgI,QAASlG,GAAa4C,SAEtBC,EAACsD,EAAe,CAAA,eAAA,MAEpBtD,EAACiD,EAAa,CACVtD,UAAWC,EACPsD,EACAC,EACAI,GAEJrL,IAAKwC,gBACO,QAAQW,KAAkB,cAC1B,cACZgI,QAAShG,GAAa0C,SAEtBC,EAACwD,EAAgB,CAAA,eAAA,UAK3BjL,IACEyH,EAAA,MAAA,CAAKL,UAAW8D,EAAc,cAAc,OAAM1D,SAC9CC,EAACC,EAAWyD,OAAM,CACd/D,UAAWgE,EACXvD,OAAO,WAAU,cACL,mBAAkBL,SAChC,GAAG9G,GAAe,KAChBd,GAAM0E,iBAMpBxE,IArLV2H,EAAA,MAAA,CACIL,UAAWiE,EACX1L,IAAKyC,GAAqB,cACd,qBACA,sBAAqBoF,SAEjCC,EAAA,MAAA,CAAKL,UAAWkE,WACX1L,GAAMsJ,KAAI,CAAC3J,EAAM0E,WACd,MAAMiC,EAAM5G,GAAaC,GACnBA,EAAKgM,aACY,UAAjBhM,EAAKgM,oBAAY,IAAA7L,EAAAA,EAAIH,EAAK2G,IAChC,OACIuB,SACIL,UAAWoE,WAGX/D,EAAA,MAAA,CACIL,UAAWqE,EAAoB,cAClBxH,IAAUvD,GAAY,cACvB,iBACZoK,QAAS,IAAMpG,GAAUT,GACzBtE,IAAMiK,GACDpI,GAAcO,QAAQkC,GAAS2F,EAAGpC,SAGtCtB,EACGuB,EAAC2C,GAAa,CACVhD,UAAWsE,EACXxF,IAAKA,EACL9B,IAAK,aAAaH,EAAQ,IAC1BsG,IAAI,UAGR9C,EAACgC,EAAgB,CACbrC,UACIsC,OArBXzF,EA0BH,WAQL,YACrB,OACIkD,EAAA,MAAA,CACIC,UAAWuE,EACXhM,IAAKqC,GAAe,qBACAe,GAAWyE,SAAA,CAE9BzE,IAAeiE,KAEfzE,GAAsB2G,KAAI,CAAC0C,EAAQ3H,IAChCwD,EAACiD,EAAa,CAEVtD,UAAWuD,EAAiB,aAChBiB,EAAOC,UAAS,cACfD,EAAO,eACpBd,QAAS,IA1RF,CACvBc,IAEItJ,IACAsJ,EAAOd,QAAQxI,GAAa5B,GAChC,EAqR+BoL,CAAmBF,GAAOpE,SAExCuE,EAAaH,EAAOI,KAAM,CAAE,eAAe,KANvC/H,MAUXhE,KAAkBX,GAAagD,KAC7BmF,EAACiD,EAAa,CACVtD,UAAWuD,EAAiB,aACP,IAAT3J,GAAa,UAAY,WACrC8J,QAASnF,YAGL8B,EADM,IAATzG,GACIiL,EAEAC,wBAKZ/L,IACGsH,EAACiD,EAAa,CACVtD,UAAWC,EACPsD,EACAwB,IACH,aACW,UACP7M,GAAagD,cACV5C,EAAA4C,GAAYM,gCAAWC,SAC3B,wBAEQ,aACZiI,QAASpF,GAAY8B,SAErBC,EAAC2E,EAAO,CAAA,eAAA,MAIhB3E,EAACiD,EAAa,CACVtD,UAAWuD,EAAiB,aAExBjI,GACM,iBACA,uBAEVoI,QAAS1K,GAAOoH,SAEhBC,EAAC4E,EAAS,CAAA,eAAA,QAEZ,EAuEDC,QAEC,EAaLC,GAA0BC,EAGrC/M"}
@@ -17,6 +17,25 @@ export interface FullscreenImageCarouselRef {
17
17
  /** Navigates to the next item, wrapping from the last item to the first. */
18
18
  goToNextItem: () => void;
19
19
  }
20
+ /**
21
+ * Custom button to be rendered in the carousel's top bar.
22
+ */
23
+ export interface FullscreenImageCarouselCustomAction {
24
+ /** Icon element rendered inside the button. */
25
+ icon: JSX.Element;
26
+ /**
27
+ * Accessible name for the button.
28
+ */
29
+ ariaLabel: string;
30
+ /**
31
+ * Called when the action is activated.
32
+ *
33
+ * @param item The item that was active when the action fired.
34
+ * @param index Zero-based index of that item in the `items` array.
35
+ */
36
+ onClick: (item: FullscreenImageCarouselItemProps, index: number) => void;
37
+ "data-testid"?: string | undefined;
38
+ }
20
39
  /**
21
40
  * Props for the `FullscreenImageCarousel`component.
22
41
  */
@@ -54,6 +73,13 @@ export interface FullscreenImageCarouselProps extends Pick<ModalV2Props, "show"
54
73
  * @default false
55
74
  */
56
75
  hideMagnifier?: boolean | undefined;
76
+ /**
77
+ * Additional icon buttons rendered in the top bar, positioned before the
78
+ * default buttons. Applies to every item.
79
+ *
80
+ * An item's own `customActions` replaces this list for that item.
81
+ */
82
+ customActions?: FullscreenImageCarouselCustomAction[] | undefined;
57
83
  /**
58
84
  * Called when the delete button is activated for the current item.
59
85
  *
@@ -68,6 +94,10 @@ export interface FullscreenImageCarouselProps extends Pick<ModalV2Props, "show"
68
94
  * of the viewport and avoid overlap with device UI elements.
69
95
  */
70
96
  insets?: Insets | undefined;
97
+ /**
98
+ * Ref to the top bar element, which holds the file info and the action buttons.
99
+ */
100
+ topBarRef?: React.Ref<HTMLDivElement> | undefined;
71
101
  }
72
102
  interface FullscreenImageCarouselBaseItemProps {
73
103
  /**
@@ -79,6 +109,13 @@ interface FullscreenImageCarouselBaseItemProps {
79
109
  * info bar (e.g. "2.4 MB").
80
110
  */
81
111
  fileSize?: string | undefined;
112
+ /**
113
+ * Replaces the component-level `customActions` while this item is active.
114
+ *
115
+ * Leave `undefined` to inherit the component-level list. Pass `[]` to
116
+ * render no custom actions for this item.
117
+ */
118
+ customActions?: FullscreenImageCarouselCustomAction[] | undefined;
82
119
  }
83
120
  /**
84
121
  * A standard image item rendered inside the carousel slide.
package/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export{Accordion}from"./accordion/accordion.js";export{Alert}from"./alert/alert.js";export{LoadingDots}from"./animations/loading-dots/loading-dots.js";export{LoadingDotsSpinner}from"./animations/loading-dots-spinner/loading-dots-spinner.js";export{LoadingSpinner}from"./animations/loading-spinner/loading-spinner.js";export{ThemedLoadingSpinner}from"./animations/themed-loading-spinner/themed-loading-spinner.js";export{Avatar}from"./avatar/avatar.js";export{Badge}from"./badge/badge.js";export{BoxContainer}from"./box-container/box-container.js";export{Breadcrumb}from"./breadcrumb/breadcrumb.js";export{Button}from"./button/button.js";export{Calendar}from"./calendar/calendar.js";export{Card}from"./card/card.js";export{Checkbox}from"./checkbox/checkbox.js";export{CountdownTimer}from"./countdown-timer/countdown-timer.js";export{DashedBorder}from"./dashed-border/dashed-border.js";export{DataTable}from"./data-table/data-table.js";export{DateInput}from"./date-input/date-input.js";export{DateNavigator}from"./date-navigator/date-navigator.js";export{DateRangeInput}from"./date-range-input/date-range-input.js";export{Divider}from"./divider/divider.js";export{Drawer}from"./drawer/drawer.js";export{ESignature}from"./e-signature/e-signature.js";export{ErrorDisplay}from"./error-display/error-display.js";export{ErrorDisplayHelper}from"./error-display/helper.js";export{FeedbackRating}from"./feedback-rating/feedback-rating.js";export{FileDownload}from"./file-download/file-download.js";export{FileUpload}from"./file-upload/file-upload.js";export{Filter}from"./filter/filter.js";export{Footer}from"./footer/footer.js";export{Form}from"./form/index.js";export{Component,FullscreenImageCarousel}from"./fullscreen-image-carousel/fullscreen-image-carousel.js";export{HistogramSlider}from"./histogram-slider/histogram-slider.js";export{ImageButton}from"./image-button/image-button.js";export{Input}from"./input/input.js";export{InputGroup}from"./input-group/input-group.js";export{InputMultiSelect}from"./input-multi-select/input-multi-select.js";export{InputNestedMultiSelect}from"./input-nested-multi-select/input-nested-multi-select.js";export{InputNestedSelect}from"./input-nested-select/input-nested-select.js";export{InputRangeSelect}from"./input-range-select/input-range-select.js";export{InputRangeSlider}from"./input-range-slider/input-range-slider.js";export{InputSelect}from"./input-select/input-select.js";export{InputSlider}from"./input-slider/input-slider.js";export{Textarea}from"./input-textarea/textarea.js";export{LanguageSwitcher}from"./language-switcher/language-switcher.js";export{Layout}from"./layout/index.js";export{LinkList}from"./link-list/link-list.js";export{Markup}from"./markup/markup.js";export{MaskedInput}from"./masked-input/masked-input.js";export{Masthead}from"./masthead/masthead.js";export{Menu,MenuTrigger}from"./menu/menu.js";export{Modal}from"./modal/index.js";export{ModalV2}from"./modal-v2/modal-v2.js";export{Navbar}from"./navbar/navbar.js";export{NBComponent,NotificationBanner}from"./notification-banner/notification-banner.js";export{withNotificationBanner}from"./notification-banner/notification-banner-hoc.js";export{OtpInput}from"./otp-input/otp-input.js";export{Overlay}from"./overlay/overlay.js";export{Pagination}from"./pagination/pagination.js";export{PhoneNumberInput}from"./phone-number-input/phone-number-input.js";export{Pill}from"./pill/pill.js";export{Popover}from"./popover/popover.js";export{PopoverInline}from"./popover/popover-inline/popover-inline.js";export{PopoverTrigger}from"./popover/popover-trigger.js";export{PredictiveTextInput}from"./predictive-text-input/predictive-text-input.js";export{ProgressIndicator}from"./progress-indicator/progress-indicator.js";export{RadioButton}from"./radio-button/radio-button.js";export{SelectHistogram}from"./select-histogram/select-histogram.js";export{Sidenav}from"./sidenav/sidenav.js";export{SingpassButton}from"./singpass-button/singpass-button.js";export{SmartAppBanner}from"./smart-app-banner/smart-app-banner.js";export{Tab}from"./tab/tab.js";export{Table}from"./table/table.js";export{Tag}from"./tag/tag.js";export{TextList}from"./text-list/index.js";export{ThemeProvider}from"./theme/theme-provider/index.js";export{useInheritedThemeScope,useTheme}from"./theme/theme-provider/hooks.js";export{Border,BorderStyle,BorderThickness}from"./theme/tokens/border.js";export{Breakpoint}from"./theme/tokens/breakpoint.js";export{Colour,PrimitiveColours,SemanticColours}from"./theme/tokens/colour.js";export{ComponentToken}from"./theme/tokens/component.js";export{Font,FontSpec}from"./theme/tokens/font.js";export{MediaQuery}from"./theme/tokens/media-query.js";export{Motion}from"./theme/tokens/motion.js";export{Radius}from"./theme/tokens/radius.js";export{Shadow}from"./theme/tokens/shadow.js";export{Spacing}from"./theme/tokens/spacing.js";export{THEME_TYPES}from"./theme/types.js";export{formatUnitValue,parsePxOrRemValue}from"./theme/utils/css-variable.js";export{useApplyStyle}from"./theme/utils/use-apply-styles.js";export{useDesignToken}from"./theme/utils/use-design-token.js";export{useMaxWidthMediaQuery,useMediaQuery,useMinWidthMediaQuery,useResolvedBreakpointToken}from"./theme/utils/use-media-query.js";export{TimeRangePicker}from"./time-range-picker/time-range-picker.js";export{TimeSlotBar}from"./time-slot-bar/time-slot-bar.js";export{TimeSlotBarWeek}from"./time-slot-bar-week/time-slot-bar-week.js";export{TimeSlotWeekView}from"./time-slot-week-view/time-slot-week-view.js";export{Timeline}from"./timeline/timeline.js";export{Timepicker}from"./timepicker/timepicker.js";export{TimeTable}from"./timetable/timetable.js";export{Toast}from"./toast/toast.js";export{Toggle}from"./toggle/toggle.js";export{Typography}from"./typography/index.js";export{UneditableSection}from"./uneditable-section/index.js";export{UnitNumberInput}from"./unit-number/unit-number-input.js";
1
+ export{Accordion}from"./accordion/accordion.js";export{Alert}from"./alert/alert.js";export{LoadingDots}from"./animations/loading-dots/loading-dots.js";export{LoadingDotsSpinner}from"./animations/loading-dots-spinner/loading-dots-spinner.js";export{LoadingSpinner}from"./animations/loading-spinner/loading-spinner.js";export{ThemedLoadingSpinner}from"./animations/themed-loading-spinner/themed-loading-spinner.js";export{Avatar}from"./avatar/avatar.js";export{Badge}from"./badge/badge.js";export{BoxContainer}from"./box-container/box-container.js";export{Breadcrumb}from"./breadcrumb/breadcrumb.js";export{Button}from"./button/button.js";export{Calendar}from"./calendar/calendar.js";export{Card}from"./card/card.js";export{Checkbox}from"./checkbox/checkbox.js";export{CountdownTimer}from"./countdown-timer/countdown-timer.js";export{DashedBorder}from"./dashed-border/dashed-border.js";export{DataTable}from"./data-table/data-table.js";export{DateInput}from"./date-input/date-input.js";export{DateNavigator}from"./date-navigator/date-navigator.js";export{DateRangeInput}from"./date-range-input/date-range-input.js";export{Divider}from"./divider/divider.js";export{Drawer}from"./drawer/drawer.js";export{ESignature}from"./e-signature/e-signature.js";export{ErrorDisplay}from"./error-display/error-display.js";export{ErrorDisplayHelper}from"./error-display/helper.js";export{FeedbackRating}from"./feedback-rating/feedback-rating.js";export{FileDownload}from"./file-download/file-download.js";export{FileUpload}from"./file-upload/file-upload.js";export{Filter}from"./filter/filter.js";export{Footer}from"./footer/footer.js";export{Form}from"./form/index.js";export{Component,FullscreenImageCarousel}from"./fullscreen-image-carousel/fullscreen-image-carousel.js";export{HistogramSlider}from"./histogram-slider/histogram-slider.js";export{ImageButton}from"./image-button/image-button.js";export{Input}from"./input/input.js";export{InputGroup}from"./input-group/input-group.js";export{InputMultiSelect}from"./input-multi-select/input-multi-select.js";export{InputNestedMultiSelect}from"./input-nested-multi-select/input-nested-multi-select.js";export{InputNestedSelect}from"./input-nested-select/input-nested-select.js";export{InputRangeSelect}from"./input-range-select/input-range-select.js";export{InputRangeSlider}from"./input-range-slider/input-range-slider.js";export{InputSelect}from"./input-select/input-select.js";export{InputSlider}from"./input-slider/input-slider.js";export{Textarea}from"./input-textarea/textarea.js";export{LanguageSwitcher}from"./language-switcher/language-switcher.js";export{Layout}from"./layout/index.js";export{LinkList}from"./link-list/link-list.js";export{Markup}from"./markup/markup.js";export{MaskedInput}from"./masked-input/masked-input.js";export{Masthead}from"./masthead/masthead.js";export{Menu,MenuTrigger}from"./menu/menu.js";export{Modal}from"./modal/index.js";export{ModalV2}from"./modal-v2/modal-v2.js";export{Navbar}from"./navbar/navbar.js";export{NBComponent,NotificationBanner}from"./notification-banner/notification-banner.js";export{withNotificationBanner}from"./notification-banner/notification-banner-hoc.js";export{OtpInput}from"./otp-input/otp-input.js";export{Overlay}from"./overlay/overlay.js";export{Pagination}from"./pagination/pagination.js";export{PhoneNumberInput}from"./phone-number-input/phone-number-input.js";export{Pill}from"./pill/pill.js";export{Popover}from"./popover/popover.js";export{PopoverInline}from"./popover/popover-inline/popover-inline.js";export{PopoverTrigger}from"./popover/popover-trigger.js";export{PredictiveTextInput}from"./predictive-text-input/predictive-text-input.js";export{ProgressIndicator}from"./progress-indicator/progress-indicator.js";export{RadioButton}from"./radio-button/radio-button.js";export{SelectHistogram}from"./select-histogram/select-histogram.js";export{Sidenav}from"./sidenav/sidenav.js";export{SingpassButton}from"./singpass-button/singpass-button.js";export{SmartAppBanner}from"./smart-app-banner/smart-app-banner.js";export{Tab}from"./tab/tab.js";export{Table}from"./table/table.js";export{Tag}from"./tag/tag.js";export{TextList}from"./text-list/index.js";export{ThemeProvider}from"./theme/theme-provider/index.js";export{useInheritedThemeScope,useTheme}from"./theme/theme-provider/hooks.js";export{Border,BorderStyle,BorderThickness}from"./theme/tokens/border.js";export{Breakpoint}from"./theme/tokens/breakpoint.js";export{Colour,PrimitiveColours,SemanticColours}from"./theme/tokens/colour.js";export{ComponentToken}from"./theme/tokens/component.js";export{Font,FontSpec}from"./theme/tokens/font.js";export{MediaQuery}from"./theme/tokens/media-query.js";export{Motion}from"./theme/tokens/motion.js";export{Radius}from"./theme/tokens/radius.js";export{Shadow}from"./theme/tokens/shadow.js";export{Spacing}from"./theme/tokens/spacing.js";export{THEME_FONT_PRESETS,THEME_TYPES}from"./theme/types.js";export{formatUnitValue,parsePxOrRemValue}from"./theme/utils/css-variable.js";export{useApplyStyle}from"./theme/utils/use-apply-styles.js";export{useDesignToken}from"./theme/utils/use-design-token.js";export{useMaxWidthMediaQuery,useMediaQuery,useMinWidthMediaQuery,useResolvedBreakpointToken}from"./theme/utils/use-media-query.js";export{TimeRangePicker}from"./time-range-picker/time-range-picker.js";export{TimeSlotBar}from"./time-slot-bar/time-slot-bar.js";export{TimeSlotBarWeek}from"./time-slot-bar-week/time-slot-bar-week.js";export{TimeSlotWeekView}from"./time-slot-week-view/time-slot-week-view.js";export{Timeline}from"./timeline/timeline.js";export{Timepicker}from"./timepicker/timepicker.js";export{TimeTable}from"./timetable/timetable.js";export{Toast}from"./toast/toast.js";export{Toggle}from"./toggle/toggle.js";export{Typography}from"./typography/index.js";export{UneditableSection}from"./uneditable-section/index.js";export{UnitNumberInput}from"./unit-number/unit-number-input.js";
2
2
  //# sourceMappingURL=index.js.map
@@ -737,7 +737,6 @@
737
737
  --sgds-elevation-edge-top: 0 -2px 4px 0 rgba(14, 14, 14, 0.08);
738
738
  --sgds-elevation-edge-bottom: 0 2px 4px 0 rgba(14, 14, 14, 0.08);
739
739
  }
740
-
741
740
  :root {
742
741
  color-scheme: only light;
743
742
  --sgds-color-scheme: only light;
@@ -737,7 +737,6 @@
737
737
  --sgds-elevation-edge-top: 0 -2px 4px 0 rgba(14, 14, 14, 0.08);
738
738
  --sgds-elevation-edge-bottom: 0 2px 4px 0 rgba(14, 14, 14, 0.08);
739
739
  }
740
-
741
740
  :root.sgds-night-theme {
742
741
  color-scheme: only dark;
743
742
  --sgds-color-scheme: only dark;