@mittwald/flow-react-components 1.5.0-next.35 → 1.5.0-next.37

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 (47) hide show
  1. package/dist/assets/component-index.json +1 -1
  2. package/dist/assets/doc-properties.json +1 -1
  3. package/dist/css/all-layered.css +1 -1
  4. package/dist/css/all.css +1 -1
  5. package/dist/js/packages/components/src/components/AlertBadge/AlertBadge.mjs +7 -4
  6. package/dist/js/packages/components/src/components/AlertBadge/AlertBadge.mjs.map +1 -1
  7. package/dist/js/packages/components/src/components/AlertBadge/AlertBadge.module.mjs +6 -2
  8. package/dist/js/packages/components/src/components/AlertBadge/AlertBadge.module.mjs.map +1 -1
  9. package/dist/js/packages/components/src/components/AlertText/AlertText.mjs +1 -1
  10. package/dist/js/packages/components/src/components/Avatar/Avatar.mjs +11 -2
  11. package/dist/js/packages/components/src/components/Avatar/Avatar.mjs.map +1 -1
  12. package/dist/js/packages/components/src/components/Avatar/Avatar.module.mjs +6 -2
  13. package/dist/js/packages/components/src/components/Avatar/Avatar.module.mjs.map +1 -1
  14. package/dist/js/packages/components/src/components/Badge/Badge.mjs +6 -3
  15. package/dist/js/packages/components/src/components/Badge/Badge.mjs.map +1 -1
  16. package/dist/js/packages/components/src/components/Badge/Badge.module.mjs +6 -2
  17. package/dist/js/packages/components/src/components/Badge/Badge.module.mjs.map +1 -1
  18. package/dist/js/packages/components/src/components/Button/Button.mjs +10 -2
  19. package/dist/js/packages/components/src/components/Button/Button.mjs.map +1 -1
  20. package/dist/js/packages/components/src/components/Button/Button.module.mjs +6 -2
  21. package/dist/js/packages/components/src/components/Button/Button.module.mjs.map +1 -1
  22. package/dist/js/packages/components/src/components/CounterBadge/CounterBadge.mjs +4 -1
  23. package/dist/js/packages/components/src/components/CounterBadge/CounterBadge.mjs.map +1 -1
  24. package/dist/js/packages/components/src/components/CounterBadge/CounterBadge.module.mjs +8 -2
  25. package/dist/js/packages/components/src/components/CounterBadge/CounterBadge.module.mjs.map +1 -1
  26. package/dist/js/packages/components/src/components/Heading/Heading.mjs +1 -1
  27. package/dist/js/packages/components/src/components/Icon/Icon.mjs +9 -0
  28. package/dist/js/packages/components/src/components/Icon/Icon.mjs.map +1 -1
  29. package/dist/js/packages/components/src/components/Icon/Icon.module.mjs +6 -2
  30. package/dist/js/packages/components/src/components/Icon/Icon.module.mjs.map +1 -1
  31. package/dist/js/packages/components/src/components/Image/Image.mjs +23 -0
  32. package/dist/js/packages/components/src/components/Image/Image.mjs.map +1 -1
  33. package/dist/js/packages/components/src/components/Image/Image.module.mjs +6 -2
  34. package/dist/js/packages/components/src/components/Image/Image.module.mjs.map +1 -1
  35. package/dist/js/packages/components/src/components/Initials/Initials.mjs +4 -1
  36. package/dist/js/packages/components/src/components/Initials/Initials.mjs.map +1 -1
  37. package/dist/js/packages/components/src/components/Initials/Initials.module.mjs +8 -2
  38. package/dist/js/packages/components/src/components/Initials/Initials.module.mjs.map +1 -1
  39. package/dist/js/packages/components/src/components/Label/Label.mjs +1 -1
  40. package/dist/js/packages/components/src/components/Link/Link.mjs +5 -2
  41. package/dist/js/packages/components/src/components/Link/Link.mjs.map +1 -1
  42. package/dist/js/packages/components/src/components/Modal/Modal.module.mjs.map +1 -1
  43. package/dist/js/packages/components/src/components/SkeletonMode/SkeletonMode.mjs +1 -1
  44. package/dist/js/packages/components/src/components/SkeletonMode/components/SkeletonRawText/SkeletonRawText.mjs +1 -1
  45. package/dist/js/packages/components/src/components/SkeletonMode/components/SkeletonTextContent/SkeletonTextContent.mjs +1 -1
  46. package/dist/js/packages/components/src/components/Text/Text.mjs +1 -1
  47. package/package.json +9 -9
@@ -5,8 +5,10 @@ import Icon_module_default from "./Icon.module.mjs";
5
5
  import { cloneElement as cloneElement$1 } from "../../lib/react/cloneElement.mjs";
6
6
  import { alphaColors, statusTypes } from "../../lib/types/props.mjs";
7
7
  import { useWarnDeprecation } from "../DeprecationWarningProvider/DeprecationWarningProvider.mjs";
8
+ import { useSkeletonMode } from "../SkeletonMode/skeletonModeContext.mjs";
8
9
  import clsx from "clsx";
9
10
  import React from "react";
11
+ import { jsx } from "react/jsx-runtime";
10
12
  //#region src/components/Icon/Icon.tsx
11
13
  var iconColors = [
12
14
  "neutral",
@@ -23,6 +25,7 @@ function isIconColor(something) {
23
25
  /** @flr-generate all */
24
26
  var Icon = flowComponent("Icon", (props) => {
25
27
  const warnDeprecation = useWarnDeprecation();
28
+ const isSkeleton = useSkeletonMode();
26
29
  const { className, "aria-label": ariaLabel, children, size = "m", status, color, ...svgAttributes } = props;
27
30
  if (status !== void 0) warnDeprecation("The 'status' prop is deprecated and will be removed in a future release. Use 'color' instead.");
28
31
  const resolvedColor = color ?? status;
@@ -43,6 +46,12 @@ var Icon = flowComponent("Icon", (props) => {
43
46
  };
44
47
  const iconElement = React.Children.toArray(children)[0];
45
48
  if (!React.isValidElement(iconElement)) throw new Error(`Expected children of Icon component to be a valid React element (got ${String(children)})`);
49
+ if (isSkeleton) return /* @__PURE__ */ jsx("span", {
50
+ className: clsx(iconProps.className, Icon_module_default.skeleton),
51
+ style: svgAttributes.style,
52
+ "aria-hidden": true,
53
+ inert: true
54
+ });
46
55
  return cloneElement$1(iconElement, iconProps);
47
56
  });
48
57
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"Icon.mjs","names":["anyIconColors","styles","cloneElement"],"sources":["../../../../../../../src/components/Icon/Icon.tsx"],"sourcesContent":["import type { PropsWithChildren, SVGAttributes } from \"react\";\nimport React from \"react\";\nimport styles from \"./Icon.module.scss\";\nimport clsx from \"clsx\";\nimport type { FlowComponentProps } from \"@/lib/componentFactory/flowComponent\";\nimport { flowComponent } from \"@/lib/componentFactory/flowComponent\";\nimport { cloneElement } from \"@/lib/react/cloneElement\";\nimport { alphaColors, type Status, statusTypes } from \"@/lib/types/props\";\nimport { useWarnDeprecation } from \"@/components/DeprecationWarningProvider\";\n\ntype SvgAttributeProps = SVGAttributes<SVGSVGElement>;\n\nconst iconColors = [\n \"neutral\",\n \"blue\",\n \"violet\",\n \"teal\",\n \"lilac\",\n ...statusTypes,\n ...alphaColors,\n] as const;\n\ntype IconColor = (typeof iconColors)[number];\ntype IconWithCustomColor = IconColor | (string & {});\n\nfunction isIconColor(something: unknown): something is IconColor {\n const anyIconColors = iconColors as readonly string[];\n return typeof something === \"string\" && anyIconColors.includes(something);\n}\n\nexport interface IconProps\n extends\n PropsWithChildren<Omit<SvgAttributeProps, \"name\" | \"color\">>,\n FlowComponentProps {\n /** The size of the icon. @default \"m\" */\n size?: \"s\" | \"m\" | \"l\";\n /**\n * The color of the icon. Besides the Flow colors, any custom CSS color is\n * supported. Inherits the surrounding text color when unset.\n */\n color?: IconWithCustomColor;\n /**\n * The elements status.\n *\n * @deprecated Use `color` instead.\n */\n status?: Status;\n}\n\n/** @flr-generate all */\nexport const Icon = flowComponent(\"Icon\", (props) => {\n const warnDeprecation = useWarnDeprecation();\n const {\n className,\n \"aria-label\": ariaLabel,\n children,\n size = \"m\",\n status,\n color,\n ...svgAttributes\n } = props;\n\n if (status !== undefined) {\n warnDeprecation(\n \"The 'status' prop is deprecated and will be removed in a future release. Use 'color' instead.\",\n );\n }\n\n const resolvedColor = color ?? status;\n const paletteColor = isIconColor(resolvedColor) ? resolvedColor : undefined;\n const customColor = paletteColor === undefined ? resolvedColor : undefined;\n const customColorStyle = customColor\n ? { color: customColor, ...svgAttributes.style }\n : undefined;\n\n const iconProps: SvgAttributeProps = {\n ...svgAttributes,\n ...(customColorStyle && { style: customColorStyle }),\n focusable: \"false\",\n role: \"img\",\n \"aria-hidden\": !ariaLabel,\n \"aria-label\": ariaLabel,\n className: clsx(\n styles.icon,\n styles[`size-${size}`],\n paletteColor && styles[paletteColor],\n className,\n ),\n };\n\n const iconElement = React.Children.toArray(children)[0];\n\n if (!React.isValidElement(iconElement)) {\n throw new Error(\n `Expected children of Icon component to be a valid React element (got ${String(\n children,\n )})`,\n );\n }\n\n return cloneElement(iconElement, iconProps);\n});\n\nexport default Icon;\n"],"mappings":";;;;;;;;;;AAYA,IAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;CACA,GAAG;CACH,GAAG;AACL;AAKA,SAAS,YAAY,WAA4C;CAE/D,OAAO,OAAO,cAAc,YAAYA,WAAc,SAAS,SAAS;AAC1E;;AAsBA,IAAa,OAAO,cAAc,SAAS,UAAU;CACnD,MAAM,kBAAkB,mBAAmB;CAC3C,MAAM,EACJ,WACA,cAAc,WACd,UACA,OAAO,KACP,QACA,OACA,GAAG,kBACD;CAEJ,IAAI,WAAW,KAAA,GACb,gBACE,+FACF;CAGF,MAAM,gBAAgB,SAAS;CAC/B,MAAM,eAAe,YAAY,aAAa,IAAI,gBAAgB,KAAA;CAClE,MAAM,cAAc,iBAAiB,KAAA,IAAY,gBAAgB,KAAA;CACjE,MAAM,mBAAmB,cACrB;EAAE,OAAO;EAAa,GAAG,cAAc;CAAM,IAC7C,KAAA;CAEJ,MAAM,YAA+B;EACnC,GAAG;EACH,GAAI,oBAAoB,EAAE,OAAO,iBAAiB;EAClD,WAAW;EACX,MAAM;EACN,eAAe,CAAC;EAChB,cAAc;EACd,WAAW,KACTC,oBAAO,MACPA,oBAAO,QAAQ,SACf,gBAAgBA,oBAAO,eACvB,SACF;CACF;CAEA,MAAM,cAAc,MAAM,SAAS,QAAQ,QAAQ,CAAC,CAAC;CAErD,IAAI,CAAC,MAAM,eAAe,WAAW,GACnC,MAAM,IAAI,MACR,wEAAwE,OACtE,QACF,EAAE,EACJ;CAGF,OAAOC,eAAa,aAAa,SAAS;AAC5C,CAAC"}
1
+ {"version":3,"file":"Icon.mjs","names":["anyIconColors","styles","cloneElement"],"sources":["../../../../../../../src/components/Icon/Icon.tsx"],"sourcesContent":["import type { PropsWithChildren, SVGAttributes } from \"react\";\nimport React from \"react\";\nimport styles from \"./Icon.module.scss\";\nimport clsx from \"clsx\";\nimport type { FlowComponentProps } from \"@/lib/componentFactory/flowComponent\";\nimport { flowComponent } from \"@/lib/componentFactory/flowComponent\";\nimport { cloneElement } from \"@/lib/react/cloneElement\";\nimport { alphaColors, type Status, statusTypes } from \"@/lib/types/props\";\nimport { useWarnDeprecation } from \"@/components/DeprecationWarningProvider\";\nimport { useSkeletonMode } from \"@/components/SkeletonMode/skeletonModeContext\";\n\ntype SvgAttributeProps = SVGAttributes<SVGSVGElement>;\n\nconst iconColors = [\n \"neutral\",\n \"blue\",\n \"violet\",\n \"teal\",\n \"lilac\",\n ...statusTypes,\n ...alphaColors,\n] as const;\n\ntype IconColor = (typeof iconColors)[number];\ntype IconWithCustomColor = IconColor | (string & {});\n\nfunction isIconColor(something: unknown): something is IconColor {\n const anyIconColors = iconColors as readonly string[];\n return typeof something === \"string\" && anyIconColors.includes(something);\n}\n\nexport interface IconProps\n extends\n PropsWithChildren<Omit<SvgAttributeProps, \"name\" | \"color\">>,\n FlowComponentProps {\n /** The size of the icon. @default \"m\" */\n size?: \"s\" | \"m\" | \"l\";\n /**\n * The color of the icon. Besides the Flow colors, any custom CSS color is\n * supported. Inherits the surrounding text color when unset.\n */\n color?: IconWithCustomColor;\n /**\n * The elements status.\n *\n * @deprecated Use `color` instead.\n */\n status?: Status;\n}\n\n/** @flr-generate all */\nexport const Icon = flowComponent(\"Icon\", (props) => {\n const warnDeprecation = useWarnDeprecation();\n const isSkeleton = useSkeletonMode();\n const {\n className,\n \"aria-label\": ariaLabel,\n children,\n size = \"m\",\n status,\n color,\n ...svgAttributes\n } = props;\n\n if (status !== undefined) {\n warnDeprecation(\n \"The 'status' prop is deprecated and will be removed in a future release. Use 'color' instead.\",\n );\n }\n\n const resolvedColor = color ?? status;\n const paletteColor = isIconColor(resolvedColor) ? resolvedColor : undefined;\n const customColor = paletteColor === undefined ? resolvedColor : undefined;\n const customColorStyle = customColor\n ? { color: customColor, ...svgAttributes.style }\n : undefined;\n\n const iconProps: SvgAttributeProps = {\n ...svgAttributes,\n ...(customColorStyle && { style: customColorStyle }),\n focusable: \"false\",\n role: \"img\",\n \"aria-hidden\": !ariaLabel,\n \"aria-label\": ariaLabel,\n className: clsx(\n styles.icon,\n styles[`size-${size}`],\n paletteColor && styles[paletteColor],\n className,\n ),\n };\n\n const iconElement = React.Children.toArray(children)[0];\n\n if (!React.isValidElement(iconElement)) {\n throw new Error(\n `Expected children of Icon component to be a valid React element (got ${String(\n children,\n )})`,\n );\n }\n\n /* An svg draws no `::after`, so the shimmer needs an element of its own. */\n if (isSkeleton) {\n return (\n <span\n className={clsx(iconProps.className, styles.skeleton)}\n style={svgAttributes.style}\n aria-hidden\n inert\n />\n );\n }\n\n return cloneElement(iconElement, iconProps);\n});\n\nexport default Icon;\n"],"mappings":";;;;;;;;;;;;AAaA,IAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;CACA,GAAG;CACH,GAAG;AACL;AAKA,SAAS,YAAY,WAA4C;CAE/D,OAAO,OAAO,cAAc,YAAYA,WAAc,SAAS,SAAS;AAC1E;;AAsBA,IAAa,OAAO,cAAc,SAAS,UAAU;CACnD,MAAM,kBAAkB,mBAAmB;CAC3C,MAAM,aAAa,gBAAgB;CACnC,MAAM,EACJ,WACA,cAAc,WACd,UACA,OAAO,KACP,QACA,OACA,GAAG,kBACD;CAEJ,IAAI,WAAW,KAAA,GACb,gBACE,+FACF;CAGF,MAAM,gBAAgB,SAAS;CAC/B,MAAM,eAAe,YAAY,aAAa,IAAI,gBAAgB,KAAA;CAClE,MAAM,cAAc,iBAAiB,KAAA,IAAY,gBAAgB,KAAA;CACjE,MAAM,mBAAmB,cACrB;EAAE,OAAO;EAAa,GAAG,cAAc;CAAM,IAC7C,KAAA;CAEJ,MAAM,YAA+B;EACnC,GAAG;EACH,GAAI,oBAAoB,EAAE,OAAO,iBAAiB;EAClD,WAAW;EACX,MAAM;EACN,eAAe,CAAC;EAChB,cAAc;EACd,WAAW,KACTC,oBAAO,MACPA,oBAAO,QAAQ,SACf,gBAAgBA,oBAAO,eACvB,SACF;CACF;CAEA,MAAM,cAAc,MAAM,SAAS,QAAQ,QAAQ,CAAC,CAAC;CAErD,IAAI,CAAC,MAAM,eAAe,WAAW,GACnC,MAAM,IAAI,MACR,wEAAwE,OACtE,QACF,EAAE,EACJ;CAIF,IAAI,YACF,OACE,oBAAC,QAAD;EACE,WAAW,KAAK,UAAU,WAAWA,oBAAO,QAAQ;EACpD,OAAO,cAAc;EACrB,eAAA;EACA,OAAA;CACD,CAAA;CAIL,OAAOC,eAAa,aAAa,SAAS;AAC5C,CAAC"}
@@ -14,6 +14,7 @@ var teal = "flow--icon--teal";
14
14
  var lilac = "flow--icon--lilac";
15
15
  var dark = "flow--icon--dark";
16
16
  var light = "flow--icon--light";
17
+ var skeleton = "flow--icon--skeleton";
17
18
  var Icon_module_default = {
18
19
  icon,
19
20
  "size-m": "flow--icon--size-m",
@@ -32,9 +33,12 @@ var Icon_module_default = {
32
33
  dark,
33
34
  light,
34
35
  "dark-static": "flow--icon--dark-static",
35
- "light-static": "flow--icon--light-static"
36
+ "light-static": "flow--icon--light-static",
37
+ skeleton,
38
+ "skeleton-surface-shimmer": "flow--icon--skeleton-surface-shimmer",
39
+ "skeleton-text-shimmer": "flow--icon--skeleton-text-shimmer"
36
40
  };
37
41
  //#endregion
38
- export { blue, danger, dark, Icon_module_default as default, icon, info, light, lilac, neutral, success, teal, unavailable, violet, warning };
42
+ export { blue, danger, dark, Icon_module_default as default, icon, info, light, lilac, neutral, skeleton, success, teal, unavailable, violet, warning };
39
43
 
40
44
  //# sourceMappingURL=Icon.module.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Icon.module.mjs","names":[],"sources":["../../../../../../../src/components/Icon/Icon.module.scss"],"sourcesContent":[".icon {\n flex-shrink: 0;\n max-width: none;\n display: inline-block;\n\n @extend .size-m;\n\n /* Sizes */\n @mixin size($size, $faPadding) {\n &:where(.size-#{$size}) {\n width: var(--icon--size--#{$size});\n height: var(--icon--size--#{$size});\n\n /*\n * FontAwesome inserts its stylesheet as an unlayered `<style>` element at\n * runtime (`autoAddCss` defaults to true), outside of Flow's build, and\n * sets `box-sizing: content-box` on `.svg-inline--fa`. Unlayered CSS beats\n * layered CSS regardless of specificity, so this override only applies in\n * the layered stylesheet variant if it leaves the cascade layer as well.\n *\n * Marked at the leaf rather than in a separate block, because these rules\n * live in a size mixin – a block would have to duplicate it.\n */\n &:global(.svg-inline--fa) {\n @layer flow.unlayered {\n box-sizing: border-box;\n padding: $faPadding;\n }\n }\n }\n }\n\n @include size(m, var(--size-rem--xxs));\n @include size(s, var(--size-rem--xxs));\n @include size(l, var(--size-rem--xs));\n\n /* Colors */\n @mixin color($color) {\n &.#{$color} {\n color: var(--icon--#{$color}-color);\n }\n }\n\n &.neutral {\n color: var(--icon--color);\n }\n\n @include color(\"danger\");\n @include color(\"success\");\n @include color(\"warning\");\n @include color(\"info\");\n @include color(\"unavailable\");\n @include color(\"blue\");\n @include color(\"violet\");\n @include color(\"teal\");\n @include color(\"lilac\");\n @include color(\"dark\");\n @include color(\"light\");\n @include color(\"dark-static\");\n @include color(\"light-static\");\n}\n"],"mappings":""}
1
+ {"version":3,"file":"Icon.module.mjs","names":[],"sources":["../../../../../../../src/components/Icon/Icon.module.scss"],"sourcesContent":["@use \"@/styles/mixins/skeleton\";\n\n.icon {\n flex-shrink: 0;\n max-width: none;\n display: inline-block;\n\n @extend .size-m;\n\n /* Sizes */\n @mixin size($size, $faPadding) {\n &:where(.size-#{$size}) {\n width: var(--icon--size--#{$size});\n height: var(--icon--size--#{$size});\n\n /*\n * FontAwesome inserts its stylesheet as an unlayered `<style>` element at\n * runtime (`autoAddCss` defaults to true), outside of Flow's build, and\n * sets `box-sizing: content-box` on `.svg-inline--fa`. Unlayered CSS beats\n * layered CSS regardless of specificity, so this override only applies in\n * the layered stylesheet variant if it leaves the cascade layer as well.\n *\n * Marked at the leaf rather than in a separate block, because these rules\n * live in a size mixin – a block would have to duplicate it.\n */\n &:global(.svg-inline--fa) {\n @layer flow.unlayered {\n box-sizing: border-box;\n padding: $faPadding;\n }\n }\n }\n }\n\n @include size(m, var(--size-rem--xxs));\n @include size(s, var(--size-rem--xxs));\n @include size(l, var(--size-rem--xs));\n\n /* Colors */\n @mixin color($color) {\n &.#{$color} {\n color: var(--icon--#{$color}-color);\n }\n }\n\n &.neutral {\n color: var(--icon--color);\n }\n\n @include color(\"danger\");\n @include color(\"success\");\n @include color(\"warning\");\n @include color(\"info\");\n @include color(\"unavailable\");\n @include color(\"blue\");\n @include color(\"violet\");\n @include color(\"teal\");\n @include color(\"lilac\");\n @include color(\"dark\");\n @include color(\"light\");\n @include color(\"dark-static\");\n @include color(\"light-static\");\n\n /* SkeletonMode */\n\n &.skeleton {\n @include skeleton.shape;\n\n --skeleton-radius: var(--corner-radius--s);\n }\n}\n\n@include skeleton.keyframes;\n"],"mappings":""}
@@ -1,14 +1,37 @@
1
1
  "use client"
2
2
  /* */
3
3
  import { flowComponent } from "../../lib/componentFactory/flowComponent.mjs";
4
+ import { useSkeletonMode } from "../SkeletonMode/skeletonModeContext.mjs";
4
5
  import Image_module_default from "./Image.module.mjs";
5
6
  import clsx from "clsx";
6
7
  import { jsx } from "react/jsx-runtime";
7
8
  //#region src/components/Image/Image.tsx
9
+ /** Aspect ratio of an image skeleton without `aspectRatio`. */
10
+ var skeletonAspectRatio = 16 / 9;
11
+ /** The `width`/`height` attribute as a CSS length — `"200"` means 200px. */
12
+ var toCssLength = (value) => typeof value === "string" && /^\d+(\.\d+)?$/.test(value) ? Number(value) : value;
8
13
  /** @flr-generate all */
9
14
  var Image = flowComponent("Image", (props) => {
10
15
  const { className, withBorder, withRoundedCorners = true, style, aspectRatio, width, height, ref, ...rest } = props;
16
+ const isSkeleton = useSkeletonMode();
11
17
  const rootClassName = clsx(Image_module_default.image, withBorder && Image_module_default.border, withRoundedCorners && Image_module_default.roundedCorners, aspectRatio && Image_module_default.aspectRatio, className);
18
+ if (isSkeleton) {
19
+ const skeletonClassName = clsx(Image_module_default.image, Image_module_default.skeleton, withBorder && Image_module_default.border, withRoundedCorners && Image_module_default.roundedCorners, className);
20
+ const skeletonWidth = toCssLength(width);
21
+ const skeletonHeight = toCssLength(height);
22
+ const skeletonStyle = {
23
+ ...style,
24
+ aspectRatio: aspectRatio ?? skeletonAspectRatio,
25
+ width: skeletonWidth ?? (skeletonHeight === void 0 ? "100%" : void 0),
26
+ height: skeletonHeight
27
+ };
28
+ return /* @__PURE__ */ jsx("span", {
29
+ className: skeletonClassName,
30
+ style: skeletonStyle,
31
+ "aria-hidden": true,
32
+ inert: true
33
+ });
34
+ }
12
35
  return /* @__PURE__ */ jsx("img", {
13
36
  ref,
14
37
  className: rootClassName,
@@ -1 +1 @@
1
- {"version":3,"file":"Image.mjs","names":["styles"],"sources":["../../../../../../../src/components/Image/Image.tsx"],"sourcesContent":["import type { FlowComponentProps } from \"@/lib/componentFactory/flowComponent\";\nimport { flowComponent } from \"@/lib/componentFactory/flowComponent\";\nimport clsx from \"clsx\";\nimport type { ComponentProps } from \"react\";\nimport styles from \"./Image.module.scss\";\n\nexport interface ImageProps\n extends\n Omit<ComponentProps<\"img\">, \"ref\">,\n FlowComponentProps<HTMLImageElement> {\n /** Display the image with a border. */\n withBorder?: boolean;\n /**\n * Display the image with rounded corners.\n *\n * @default true\n */\n withRoundedCorners?: boolean;\n /**\n * The aspect ratio of the images container. Larger images will be centered\n * and their overflow will be hidden.\n */\n aspectRatio?: number;\n}\n\n/** @flr-generate all */\nexport const Image = flowComponent(\"Image\", (props) => {\n const {\n className,\n withBorder,\n withRoundedCorners = true,\n style,\n aspectRatio,\n width,\n height,\n ref,\n ...rest\n } = props;\n\n const rootClassName = clsx(\n styles.image,\n withBorder && styles.border,\n withRoundedCorners && styles.roundedCorners,\n aspectRatio && styles.aspectRatio,\n className,\n );\n\n return (\n <img\n ref={ref}\n className={rootClassName}\n style={{\n ...style,\n ...(aspectRatio !== undefined && { aspectRatio }),\n ...(width !== undefined && { width }),\n ...(height !== undefined && { height }),\n }}\n {...rest}\n />\n );\n});\n\nexport default Image;\n"],"mappings":";;;;;;;;AA0BA,IAAa,QAAQ,cAAc,UAAU,UAAU;CACrD,MAAM,EACJ,WACA,YACA,qBAAqB,MACrB,OACA,aACA,OACA,QACA,KACA,GAAG,SACD;CAEJ,MAAM,gBAAgB,KACpBA,qBAAO,OACP,cAAcA,qBAAO,QACrB,sBAAsBA,qBAAO,gBAC7B,eAAeA,qBAAO,aACtB,SACF;CAEA,OACE,oBAAC,OAAD;EACO;EACL,WAAW;EACX,OAAO;GACL,GAAG;GACH,GAAI,gBAAgB,KAAA,KAAa,EAAE,YAAY;GAC/C,GAAI,UAAU,KAAA,KAAa,EAAE,MAAM;GACnC,GAAI,WAAW,KAAA,KAAa,EAAE,OAAO;EACvC;EACA,GAAI;CACL,CAAA;AAEL,CAAC"}
1
+ {"version":3,"file":"Image.mjs","names":["styles"],"sources":["../../../../../../../src/components/Image/Image.tsx"],"sourcesContent":["import type { FlowComponentProps } from \"@/lib/componentFactory/flowComponent\";\nimport { flowComponent } from \"@/lib/componentFactory/flowComponent\";\nimport clsx from \"clsx\";\nimport type { ComponentProps, CSSProperties } from \"react\";\nimport styles from \"./Image.module.scss\";\nimport { useSkeletonMode } from \"@/components/SkeletonMode/skeletonModeContext\";\n\n/** Aspect ratio of an image skeleton without `aspectRatio`. */\nconst skeletonAspectRatio = 16 / 9;\n\n/** The `width`/`height` attribute as a CSS length — `\"200\"` means 200px. */\nconst toCssLength = (value: number | string | undefined) =>\n typeof value === \"string\" && /^\\d+(\\.\\d+)?$/.test(value)\n ? Number(value)\n : value;\n\nexport interface ImageProps\n extends\n Omit<ComponentProps<\"img\">, \"ref\">,\n FlowComponentProps<HTMLImageElement> {\n /** Display the image with a border. */\n withBorder?: boolean;\n /**\n * Display the image with rounded corners.\n *\n * @default true\n */\n withRoundedCorners?: boolean;\n /**\n * The aspect ratio of the images container. Larger images will be centered\n * and their overflow will be hidden.\n */\n aspectRatio?: number;\n}\n\n/** @flr-generate all */\nexport const Image = flowComponent(\"Image\", (props) => {\n const {\n className,\n withBorder,\n withRoundedCorners = true,\n style,\n aspectRatio,\n width,\n height,\n ref,\n ...rest\n } = props;\n\n const isSkeleton = useSkeletonMode();\n\n const rootClassName = clsx(\n styles.image,\n withBorder && styles.border,\n withRoundedCorners && styles.roundedCorners,\n aspectRatio && styles.aspectRatio,\n className,\n );\n\n /* No `<img>`, so the skeleton issues no request. Without both dimensions\n the aspect ratio sizes it, filling the width when it has neither. */\n if (isSkeleton) {\n const skeletonClassName = clsx(\n styles.image,\n styles.skeleton,\n withBorder && styles.border,\n withRoundedCorners && styles.roundedCorners,\n className,\n );\n\n const skeletonWidth = toCssLength(width);\n const skeletonHeight = toCssLength(height);\n\n const skeletonStyle: CSSProperties = {\n ...style,\n aspectRatio: aspectRatio ?? skeletonAspectRatio,\n width:\n skeletonWidth ?? (skeletonHeight === undefined ? \"100%\" : undefined),\n height: skeletonHeight,\n };\n\n return (\n <span\n className={skeletonClassName}\n style={skeletonStyle}\n aria-hidden\n inert\n />\n );\n }\n\n return (\n <img\n ref={ref}\n className={rootClassName}\n style={{\n ...style,\n ...(aspectRatio !== undefined && { aspectRatio }),\n ...(width !== undefined && { width }),\n ...(height !== undefined && { height }),\n }}\n {...rest}\n />\n );\n});\n\nexport default Image;\n"],"mappings":";;;;;;;;;AAQA,IAAM,sBAAsB,KAAK;;AAGjC,IAAM,eAAe,UACnB,OAAO,UAAU,YAAY,gBAAgB,KAAK,KAAK,IACnD,OAAO,KAAK,IACZ;;AAsBN,IAAa,QAAQ,cAAc,UAAU,UAAU;CACrD,MAAM,EACJ,WACA,YACA,qBAAqB,MACrB,OACA,aACA,OACA,QACA,KACA,GAAG,SACD;CAEJ,MAAM,aAAa,gBAAgB;CAEnC,MAAM,gBAAgB,KACpBA,qBAAO,OACP,cAAcA,qBAAO,QACrB,sBAAsBA,qBAAO,gBAC7B,eAAeA,qBAAO,aACtB,SACF;CAIA,IAAI,YAAY;EACd,MAAM,oBAAoB,KACxBA,qBAAO,OACPA,qBAAO,UACP,cAAcA,qBAAO,QACrB,sBAAsBA,qBAAO,gBAC7B,SACF;EAEA,MAAM,gBAAgB,YAAY,KAAK;EACvC,MAAM,iBAAiB,YAAY,MAAM;EAEzC,MAAM,gBAA+B;GACnC,GAAG;GACH,aAAa,eAAe;GAC5B,OACE,kBAAkB,mBAAmB,KAAA,IAAY,SAAS,KAAA;GAC5D,QAAQ;EACV;EAEA,OACE,oBAAC,QAAD;GACE,WAAW;GACX,OAAO;GACP,eAAA;GACA,OAAA;EACD,CAAA;CAEL;CAEA,OACE,oBAAC,OAAD;EACO;EACL,WAAW;EACX,OAAO;GACL,GAAG;GACH,GAAI,gBAAgB,KAAA,KAAa,EAAE,YAAY;GAC/C,GAAI,UAAU,KAAA,KAAa,EAAE,MAAM;GACnC,GAAI,WAAW,KAAA,KAAa,EAAE,OAAO;EACvC;EACA,GAAI;CACL,CAAA;AAEL,CAAC"}
@@ -5,13 +5,17 @@ var image = "flow--image";
5
5
  var border = "flow--image--border";
6
6
  var roundedCorners = "flow--image--rounded-corners";
7
7
  var aspectRatio = "flow--image--aspect-ratio";
8
+ var skeleton = "flow--image--skeleton";
8
9
  var Image_module_default = {
9
10
  image,
10
11
  border,
11
12
  roundedCorners,
12
- aspectRatio
13
+ aspectRatio,
14
+ skeleton,
15
+ "skeleton-surface-shimmer": "flow--image--skeleton-surface-shimmer",
16
+ "skeleton-text-shimmer": "flow--image--skeleton-text-shimmer"
13
17
  };
14
18
  //#endregion
15
- export { aspectRatio, border, Image_module_default as default, image, roundedCorners };
19
+ export { aspectRatio, border, Image_module_default as default, image, roundedCorners, skeleton };
16
20
 
17
21
  //# sourceMappingURL=Image.module.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Image.module.mjs","names":[],"sources":["../../../../../../../src/components/Image/Image.module.scss"],"sourcesContent":[".image {\n max-width: 100%;\n\n &.border {\n border-width: var(--image--border-width);\n border-style: var(--image--border-style);\n border-color: var(--image--border-color);\n }\n\n &.roundedCorners {\n border-radius: var(--image--corner-radius);\n overflow: hidden;\n }\n\n &:where(.aspectRatio) {\n overflow-clip-margin: unset;\n object-fit: cover;\n width: fit-content;\n overflow: hidden;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"Image.module.mjs","names":[],"sources":["../../../../../../../src/components/Image/Image.module.scss"],"sourcesContent":["@use \"@/styles/mixins/skeleton\";\n\n.image {\n max-width: 100%;\n\n &.border {\n border-width: var(--image--border-width);\n border-style: var(--image--border-style);\n border-color: var(--image--border-color);\n }\n\n &.roundedCorners {\n border-radius: var(--image--corner-radius);\n overflow: hidden;\n }\n\n &:where(.aspectRatio) {\n overflow-clip-margin: unset;\n object-fit: cover;\n width: fit-content;\n overflow: hidden;\n }\n\n /* SkeletonMode */\n\n &.skeleton {\n @include skeleton.shape;\n\n --skeleton-radius: 0;\n\n display: inline-block;\n vertical-align: middle;\n }\n\n &.skeleton.roundedCorners {\n --skeleton-radius: var(--image--corner-radius);\n }\n}\n\n@include skeleton.keyframes;\n"],"mappings":""}
@@ -2,6 +2,7 @@
2
2
  /* */
3
3
  import { flowComponent } from "../../lib/componentFactory/flowComponent.mjs";
4
4
  import { extractTextFromFirstChild } from "../../lib/react/remote.mjs";
5
+ import { useSkeletonMode } from "../SkeletonMode/skeletonModeContext.mjs";
5
6
  import { getInitialsFromString } from "./lib/getInitialsFromString.mjs";
6
7
  import Initials_module_default from "./Initials.module.mjs";
7
8
  import { getColorFromInitials } from "./lib/getColorFromInitials.mjs";
@@ -12,10 +13,11 @@ import { jsx } from "react/jsx-runtime";
12
13
  /** @flr-generate all */
13
14
  var Initials = flowComponent("Initials", (props) => {
14
15
  const { children, useDynamicColor = false, className, "aria-hidden": ariaHidden, ref } = props;
16
+ const isSkeleton = useSkeletonMode();
15
17
  const textContent = extractTextFromFirstChild(children) ?? "";
16
18
  const initials = getInitialsFromString(textContent);
17
19
  const dynamicColor = useMemo(() => useDynamicColor ? getColorFromInitials(textContent) : void 0, [useDynamicColor, textContent]);
18
- const rootClassName = clsx(Initials_module_default.initials, className);
20
+ const rootClassName = clsx(Initials_module_default.initials, isSkeleton && Initials_module_default.skeleton, className);
19
21
  const initialsElements = initials.map((initial, index) => /* @__PURE__ */ jsx("span", {
20
22
  "aria-hidden": true,
21
23
  role: "presentation",
@@ -28,6 +30,7 @@ var Initials = flowComponent("Initials", (props) => {
28
30
  className: rootClassName,
29
31
  ref,
30
32
  role: "img",
33
+ inert: isSkeleton || void 0,
31
34
  children: initialsElements
32
35
  });
33
36
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Initials.mjs","names":["styles"],"sources":["../../../../../../../src/components/Initials/Initials.tsx"],"sourcesContent":["import { type PropsWithChildren, useMemo } from \"react\";\nimport { getInitialsFromString } from \"./lib/getInitialsFromString\";\nimport styles from \"./Initials.module.scss\";\nimport clsx from \"clsx\";\nimport type { FlowComponentProps } from \"@/lib/componentFactory/flowComponent\";\nimport { flowComponent } from \"@/lib/componentFactory/flowComponent\";\nimport type { PropsWithClassName } from \"@/lib/types/props\";\nimport { getColorFromInitials } from \"@/components/Initials/lib/getColorFromInitials\";\nimport { extractTextFromFirstChild } from \"@/lib/react/remote\";\n\nexport interface InitialsProps\n extends PropsWithChildren, PropsWithClassName, FlowComponentProps {\n \"aria-hidden\"?: boolean;\n /** @internal */\n useDynamicColor?: boolean;\n}\n\n/** @flr-generate all */\nexport const Initials = flowComponent(\"Initials\", (props) => {\n const {\n children,\n useDynamicColor = false,\n className,\n \"aria-hidden\": ariaHidden,\n ref,\n } = props;\n\n const textContent = extractTextFromFirstChild(children) ?? \"\";\n const initials = getInitialsFromString(textContent);\n const dynamicColor = useMemo(\n () => (useDynamicColor ? getColorFromInitials(textContent) : undefined),\n [useDynamicColor, textContent],\n );\n\n const rootClassName = clsx(styles.initials, className);\n\n const initialsElements = initials.map((initial, index) => (\n <span aria-hidden role=\"presentation\" key={index}>\n {initial}\n </span>\n ));\n\n return (\n <div\n data-dynamic-color={dynamicColor}\n aria-hidden={ariaHidden}\n aria-label={ariaHidden ? undefined : textContent}\n className={rootClassName}\n ref={ref}\n role=\"img\"\n >\n {initialsElements}\n </div>\n );\n});\n\nexport default Initials;\n"],"mappings":";;;;;;;;;;;;AAkBA,IAAa,WAAW,cAAc,aAAa,UAAU;CAC3D,MAAM,EACJ,UACA,kBAAkB,OAClB,WACA,eAAe,YACf,QACE;CAEJ,MAAM,cAAc,0BAA0B,QAAQ,KAAK;CAC3D,MAAM,WAAW,sBAAsB,WAAW;CAClD,MAAM,eAAe,cACZ,kBAAkB,qBAAqB,WAAW,IAAI,KAAA,GAC7D,CAAC,iBAAiB,WAAW,CAC/B;CAEA,MAAM,gBAAgB,KAAKA,wBAAO,UAAU,SAAS;CAErD,MAAM,mBAAmB,SAAS,KAAK,SAAS,UAC9C,oBAAC,QAAD;EAAM,eAAA;EAAY,MAAK;EACpB,UAAA;CACG,GAFqC,KAErC,CACP;CAED,OACE,oBAAC,OAAD;EACE,sBAAoB;EACpB,eAAa;EACb,cAAY,aAAa,KAAA,IAAY;EACrC,WAAW;EACN;EACL,MAAK;EAEJ,UAAA;CACE,CAAA;AAET,CAAC"}
1
+ {"version":3,"file":"Initials.mjs","names":["styles"],"sources":["../../../../../../../src/components/Initials/Initials.tsx"],"sourcesContent":["import { type PropsWithChildren, useMemo } from \"react\";\nimport { getInitialsFromString } from \"./lib/getInitialsFromString\";\nimport styles from \"./Initials.module.scss\";\nimport clsx from \"clsx\";\nimport type { FlowComponentProps } from \"@/lib/componentFactory/flowComponent\";\nimport { flowComponent } from \"@/lib/componentFactory/flowComponent\";\nimport type { PropsWithClassName } from \"@/lib/types/props\";\nimport { getColorFromInitials } from \"@/components/Initials/lib/getColorFromInitials\";\nimport { extractTextFromFirstChild } from \"@/lib/react/remote\";\nimport { useSkeletonMode } from \"@/components/SkeletonMode/skeletonModeContext\";\n\nexport interface InitialsProps\n extends PropsWithChildren, PropsWithClassName, FlowComponentProps {\n \"aria-hidden\"?: boolean;\n /** @internal */\n useDynamicColor?: boolean;\n}\n\n/** @flr-generate all */\nexport const Initials = flowComponent(\"Initials\", (props) => {\n const {\n children,\n useDynamicColor = false,\n className,\n \"aria-hidden\": ariaHidden,\n ref,\n } = props;\n\n const isSkeleton = useSkeletonMode();\n\n const textContent = extractTextFromFirstChild(children) ?? \"\";\n const initials = getInitialsFromString(textContent);\n const dynamicColor = useMemo(\n () => (useDynamicColor ? getColorFromInitials(textContent) : undefined),\n [useDynamicColor, textContent],\n );\n\n const rootClassName = clsx(\n styles.initials,\n isSkeleton && styles.skeleton,\n className,\n );\n\n const initialsElements = initials.map((initial, index) => (\n <span aria-hidden role=\"presentation\" key={index}>\n {initial}\n </span>\n ));\n\n return (\n <div\n data-dynamic-color={dynamicColor}\n aria-hidden={ariaHidden}\n aria-label={ariaHidden ? undefined : textContent}\n className={rootClassName}\n ref={ref}\n role=\"img\"\n inert={isSkeleton || undefined}\n >\n {initialsElements}\n </div>\n );\n});\n\nexport default Initials;\n"],"mappings":";;;;;;;;;;;;;AAmBA,IAAa,WAAW,cAAc,aAAa,UAAU;CAC3D,MAAM,EACJ,UACA,kBAAkB,OAClB,WACA,eAAe,YACf,QACE;CAEJ,MAAM,aAAa,gBAAgB;CAEnC,MAAM,cAAc,0BAA0B,QAAQ,KAAK;CAC3D,MAAM,WAAW,sBAAsB,WAAW;CAClD,MAAM,eAAe,cACZ,kBAAkB,qBAAqB,WAAW,IAAI,KAAA,GAC7D,CAAC,iBAAiB,WAAW,CAC/B;CAEA,MAAM,gBAAgB,KACpBA,wBAAO,UACP,cAAcA,wBAAO,UACrB,SACF;CAEA,MAAM,mBAAmB,SAAS,KAAK,SAAS,UAC9C,oBAAC,QAAD;EAAM,eAAA;EAAY,MAAK;EACpB,UAAA;CACG,GAFqC,KAErC,CACP;CAED,OACE,oBAAC,OAAD;EACE,sBAAoB;EACpB,eAAa;EACb,cAAY,aAAa,KAAA,IAAY;EACrC,WAAW;EACN;EACL,MAAK;EACL,OAAO,cAAc,KAAA;EAEpB,UAAA;CACE,CAAA;AAET,CAAC"}
@@ -2,8 +2,14 @@
2
2
  /* */
3
3
  //#region src/components/Initials/Initials.module.scss
4
4
  var initials = "flow--initials";
5
- var Initials_module_default = { initials };
5
+ var skeleton = "flow--initials--skeleton";
6
+ var Initials_module_default = {
7
+ initials,
8
+ skeleton,
9
+ "skeleton-surface-shimmer": "flow--initials--skeleton-surface-shimmer",
10
+ "skeleton-text-shimmer": "flow--initials--skeleton-text-shimmer"
11
+ };
6
12
  //#endregion
7
- export { Initials_module_default as default, initials };
13
+ export { Initials_module_default as default, initials, skeleton };
8
14
 
9
15
  //# sourceMappingURL=Initials.module.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Initials.module.mjs","names":[],"sources":["../../../../../../../src/components/Initials/Initials.module.scss"],"sourcesContent":[".initials {\n display: flex;\n font-weight: var(--initials--font-weight);\n\n *:nth-child(even) {\n color: var(--content-color);\n }\n\n *:nth-child(odd) {\n color: var(--content-accent-color);\n }\n\n *:only-child {\n color: var(--content-color);\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"Initials.module.mjs","names":[],"sources":["../../../../../../../src/components/Initials/Initials.module.scss"],"sourcesContent":["@use \"@/styles/mixins/skeleton\";\n\n.initials {\n display: flex;\n font-weight: var(--initials--font-weight);\n\n *:nth-child(even) {\n color: var(--content-color);\n }\n\n *:nth-child(odd) {\n color: var(--content-accent-color);\n }\n\n *:only-child {\n color: var(--content-color);\n }\n\n /* SkeletonMode */\n\n &.skeleton {\n @include skeleton.shape;\n }\n}\n\n@include skeleton.keyframes;\n"],"mappings":""}
@@ -3,9 +3,9 @@
3
3
  import { PropsContextProvider } from "../../lib/propsContext/components/PropsContextProvider.mjs";
4
4
  import { flowComponent } from "../../lib/componentFactory/flowComponent.mjs";
5
5
  import overlayTriggersTunneledTo from "../../lib/propsContext/overlayTriggerTunnel.mjs";
6
+ import { useSkeletonMode } from "../SkeletonMode/skeletonModeContext.mjs";
6
7
  import { useLocalizedStringFormatter } from "../TranslationProvider/useLocalizedStringFormatter.mjs";
7
8
  import { UiComponentTunnelExit } from "../UiComponentTunnel/UiComponentTunnelExit.mjs";
8
- import { useSkeletonMode } from "../SkeletonMode/skeletonModeContext.mjs";
9
9
  import { SkeletonTextContent } from "../SkeletonMode/components/SkeletonTextContent/SkeletonTextContent.mjs";
10
10
  import Label_module_default from "./Label.module.mjs";
11
11
  import __locale_default from "../../../../../_virtual/_8.locale.mjs";
@@ -3,9 +3,11 @@
3
3
  import { PropsContextProvider } from "../../lib/propsContext/components/PropsContextProvider.mjs";
4
4
  import { flowComponent } from "../../lib/componentFactory/flowComponent.mjs";
5
5
  import dynamic from "../../lib/propsContext/dynamicProps/dynamic.mjs";
6
+ import { containsTextChild } from "../../lib/react/remote.mjs";
6
7
  import { isAlphaColor } from "../../lib/types/props.mjs";
7
- import { UiComponentTunnelExit } from "../UiComponentTunnel/UiComponentTunnelExit.mjs";
8
8
  import { useSkeletonMode } from "../SkeletonMode/skeletonModeContext.mjs";
9
+ import { UiComponentTunnelExit } from "../UiComponentTunnel/UiComponentTunnelExit.mjs";
10
+ import { hasContent } from "../SkeletonMode/lib/hasContent.mjs";
9
11
  import { SkeletonTextContent } from "../SkeletonMode/components/SkeletonTextContent/SkeletonTextContent.mjs";
10
12
  import Link_module_default from "./Link.module.mjs";
11
13
  import { linkContext } from "./context.mjs";
@@ -61,9 +63,10 @@ var Link = flowComponent("Link", (props) => {
61
63
  }
62
64
  };
63
65
  const unsupportedTypingsLinkProps = ariaCurrent ? { "aria-current": true } : {};
66
+ const hasOnlyElements = hasContent(children) && !containsTextChild(children);
64
67
  const content = /* @__PURE__ */ jsxs(PropsContextProvider, {
65
68
  props: propsContext,
66
- children: [/* @__PURE__ */ jsx(SkeletonTextContent, {
69
+ children: [hasOnlyElements ? children : /* @__PURE__ */ jsx(SkeletonTextContent, {
67
70
  defaultWidth: "6em",
68
71
  children
69
72
  }), /* @__PURE__ */ jsx(LinkIcon, {
@@ -1 +1 @@
1
- {"version":3,"file":"Link.mjs","names":["styles","_Fragment"],"sources":["../../../../../../../src/components/Link/Link.tsx"],"sourcesContent":["import type {\n ComponentProps,\n ComponentType,\n CSSProperties,\n MouseEvent,\n PropsWithChildren,\n} from \"react\";\nimport { useContext } from \"react\";\nimport * as Aria from \"react-aria-components\";\nimport { dynamic, type PropsContext } from \"@/lib/propsContext\";\nimport { PropsContextProvider } from \"@/lib/propsContext\";\nimport styles from \"./Link.module.scss\";\nimport clsx from \"clsx\";\nimport type { FlowComponentProps } from \"@/lib/componentFactory/flowComponent\";\nimport { flowComponent } from \"@/lib/componentFactory/flowComponent\";\nimport {\n type AlphaColor,\n isAlphaColor,\n type PropsWithClassName,\n} from \"@/lib/types/props\";\nimport { linkContext } from \"@/components/Link/context\";\nimport { LinkIcon } from \"@/components/Link/components/LinkIcon\";\nimport { handleLinkClick, useRouter } from \"@react-aria/utils\";\nimport { UiComponentTunnelExit } from \"@/components/UiComponentTunnel/UiComponentTunnelExit\";\nimport { SkeletonTextContent } from \"@/components/SkeletonMode/components/SkeletonTextContent\";\nimport { useSkeletonMode } from \"@/components/SkeletonMode/skeletonModeContext\";\n\nexport interface LinkProps\n extends\n PropsWithChildren<Omit<Aria.LinkProps, \"children\" | \"slot\" | \"className\">>,\n FlowComponentProps<HTMLAnchorElement>,\n PropsWithClassName {\n /** Whether the link should be styled for being displayed inside a text. */\n inline?: boolean;\n /** An alternative link component. */\n linkComponent?: ComponentType<Omit<ComponentProps<\"a\">, \"ref\">>;\n /** The color of the link. @default \"default\" */\n color?: \"default\" | AlphaColor;\n /** Marks the link as the currently active one, e.g. in a navigation. */\n \"aria-current\"?: string;\n /**\n * The name of the slot the link is placed in. In action groups: `primary` (at\n * the end), `secondary` (before the primary action) or `abort` (at the\n * start).\n */\n slot?: string;\n /** The whiteSpace css value of the element. */\n whiteSpace?: CSSProperties[\"whiteSpace\"];\n /** The size of the element. @default \"m\" */\n size?: \"s\" | \"m\";\n /** @internal */\n unstyled?: boolean;\n}\n\n/** @flr-generate all */\nexport const Link = flowComponent(\"Link\", (props) => {\n const {\n children,\n className,\n inline,\n linkComponent: linkComponentFromProps,\n color = \"default\",\n unstyled = false,\n \"aria-current\": ariaCurrent,\n ref,\n slot: ignoredSlotProp,\n style,\n whiteSpace,\n size = \"m\",\n onClickCapture: onClickCaptureFromProps,\n target,\n download,\n ...rest\n } = props;\n\n const { linkComponent: linkComponentFromContext } = useContext(linkContext);\n const isSkeleton = useSkeletonMode();\n const Link = linkComponentFromProps\n ? (linkComponentFromProps as typeof Aria.Link)\n : props.href && linkComponentFromContext\n ? (linkComponentFromContext as typeof Aria.Link)\n : Aria.Link;\n\n const router = useRouter();\n\n /**\n * An interactive child (e.g. a Button) stops click propagation via React\n * Aria's `usePress`, so the anchor's own `onClick` never runs and navigation\n * falls back to a full page load. Handling it in the capture phase (top-down,\n * before the child) keeps navigation client-side. Only needed for React\n * Aria's `<a>`; custom link components handle navigation themselves.\n */\n const handleClickCapture =\n Link === Aria.Link && props.href\n ? (event: MouseEvent<HTMLAnchorElement>) => {\n onClickCaptureFromProps?.(event);\n if (event.target !== event.currentTarget) {\n handleLinkClick(event, router, props.href, props.routerOptions);\n }\n }\n : onClickCaptureFromProps;\n\n const rootClassName = unstyled\n ? className\n : clsx(\n styles.link,\n inline && styles.inline,\n isAlphaColor(color) && styles[color],\n size === \"s\" && styles[\"size-s\"],\n className,\n );\n\n const propsContext: PropsContext = {\n Icon: {\n tunnel: {\n component: \"Link\",\n id: \"icon\",\n },\n className: styles.icon,\n size: \"s\",\n },\n AlertText: {\n className: styles.alertText,\n },\n AccentBox: { className: styles.accentBox },\n LayoutCard: { className: styles.layoutCard },\n Button: {\n elementType: \"span\",\n className: styles.button,\n size: props.size,\n color: props.color === \"default\" ? undefined : props.color,\n isDisabled: props.isDisabled,\n children: dynamic((buttonProps) => (\n <>\n {buttonProps.children}\n <LinkIcon download={download} target={target} unstyled={unstyled} />\n </>\n )),\n },\n };\n\n const unsupportedTypingsLinkProps = ariaCurrent\n ? ({\n \"aria-current\": true,\n } as Record<string, unknown>)\n : {};\n\n const content = (\n <PropsContextProvider props={propsContext}>\n <SkeletonTextContent defaultWidth=\"6em\">{children}</SkeletonTextContent>\n <LinkIcon\n withZeroWidthJoiner\n unstyled={unstyled}\n target={target}\n download={download}\n >\n <UiComponentTunnelExit id={\"icon\"} component={\"Link\"} />\n </LinkIcon>\n </PropsContextProvider>\n );\n\n /* react-aria's Link drops `inert`, and a skeleton needs no anchor. */\n if (isSkeleton) {\n return (\n <span className={rootClassName} style={{ ...style, whiteSpace }} inert>\n {content}\n </span>\n );\n }\n\n return (\n <Link\n {...unsupportedTypingsLinkProps}\n {...rest}\n target={target}\n download={download}\n onClickCapture={handleClickCapture}\n className={rootClassName}\n ref={ref}\n style={{ ...style, whiteSpace }}\n >\n {content}\n </Link>\n );\n});\n\nexport default Link;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuDA,IAAa,OAAO,cAAc,SAAS,UAAU;CACnD,MAAM,EACJ,UACA,WACA,QACA,eAAe,wBACf,QAAQ,WACR,WAAW,OACX,gBAAgB,aAChB,KACA,MAAM,iBACN,OACA,YACA,OAAO,KACP,gBAAgB,yBAChB,QACA,UACA,GAAG,SACD;CAEJ,MAAM,EAAE,eAAe,6BAA6B,WAAW,WAAW;CAC1E,MAAM,aAAa,gBAAgB;CACnC,MAAM,OAAO,yBACR,yBACD,MAAM,QAAQ,2BACX,2BACD,KAAK;CAEX,MAAM,SAAS,UAAU;;;;;;;;CASzB,MAAM,qBACJ,SAAS,KAAK,QAAQ,MAAM,QACvB,UAAyC;EACxC,0BAA0B,KAAK;EAC/B,IAAI,MAAM,WAAW,MAAM,eACzB,gBAAgB,OAAO,QAAQ,MAAM,MAAM,MAAM,aAAa;CAElE,IACA;CAEN,MAAM,gBAAgB,WAClB,YACA,KACEA,oBAAO,MACP,UAAUA,oBAAO,QACjB,aAAa,KAAK,KAAKA,oBAAO,QAC9B,SAAS,OAAOA,oBAAO,WACvB,SACF;CAEJ,MAAM,eAA6B;EACjC,MAAM;GACJ,QAAQ;IACN,WAAW;IACX,IAAI;GACN;GACA,WAAWA,oBAAO;GAClB,MAAM;EACR;EACA,WAAW,EACT,WAAWA,oBAAO,UACpB;EACA,WAAW,EAAE,WAAWA,oBAAO,UAAU;EACzC,YAAY,EAAE,WAAWA,oBAAO,WAAW;EAC3C,QAAQ;GACN,aAAa;GACb,WAAWA,oBAAO;GAClB,MAAM,MAAM;GACZ,OAAO,MAAM,UAAU,YAAY,KAAA,IAAY,MAAM;GACrD,YAAY,MAAM;GAClB,UAAU,SAAS,gBACjB,qBAAAC,YAAA,EAAA,UAAA,CACG,YAAY,UACb,oBAAC,UAAD;IAAoB;IAAkB;IAAkB;GAAW,CAAA,CACnE,EAAA,CAAA,CACH;EACH;CACF;CAEA,MAAM,8BAA8B,cAC/B,EACC,gBAAgB,KAClB,IACA,CAAC;CAEL,MAAM,UACJ,qBAAC,sBAAD;EAAsB,OAAO;EAA7B,UAAA,CACE,oBAAC,qBAAD;GAAqB,cAAa;GAAO;EAA8B,CAAA,GACvE,oBAAC,UAAD;GACE,qBAAA;GACU;GACF;GACE;GAEV,UAAA,oBAAC,uBAAD;IAAuB,IAAI;IAAQ,WAAW;GAAS,CAAA;EAC/C,CAAA,CACU;;CAIxB,IAAI,YACF,OACE,oBAAC,QAAD;EAAM,WAAW;EAAe,OAAO;GAAE,GAAG;GAAO;EAAW;EAAG,OAAA;EAC9D,UAAA;CACG,CAAA;CAIV,OACE,oBAAC,MAAD;EACE,GAAI;EACJ,GAAI;EACI;EACE;EACV,gBAAgB;EAChB,WAAW;EACN;EACL,OAAO;GAAE,GAAG;GAAO;EAAW;EAE7B,UAAA;CACG,CAAA;AAEV,CAAC"}
1
+ {"version":3,"file":"Link.mjs","names":["styles","_Fragment","_jsx"],"sources":["../../../../../../../src/components/Link/Link.tsx"],"sourcesContent":["import type {\n ComponentProps,\n ComponentType,\n CSSProperties,\n MouseEvent,\n PropsWithChildren,\n} from \"react\";\nimport { useContext } from \"react\";\nimport * as Aria from \"react-aria-components\";\nimport { dynamic, type PropsContext } from \"@/lib/propsContext\";\nimport { PropsContextProvider } from \"@/lib/propsContext\";\nimport styles from \"./Link.module.scss\";\nimport clsx from \"clsx\";\nimport type { FlowComponentProps } from \"@/lib/componentFactory/flowComponent\";\nimport { flowComponent } from \"@/lib/componentFactory/flowComponent\";\nimport {\n type AlphaColor,\n isAlphaColor,\n type PropsWithClassName,\n} from \"@/lib/types/props\";\nimport { linkContext } from \"@/components/Link/context\";\nimport { LinkIcon } from \"@/components/Link/components/LinkIcon\";\nimport { handleLinkClick, useRouter } from \"@react-aria/utils\";\nimport { UiComponentTunnelExit } from \"@/components/UiComponentTunnel/UiComponentTunnelExit\";\nimport { SkeletonTextContent } from \"@/components/SkeletonMode/components/SkeletonTextContent\";\nimport { useSkeletonMode } from \"@/components/SkeletonMode/skeletonModeContext\";\nimport { hasContent } from \"@/components/SkeletonMode/lib/hasContent\";\nimport { containsTextChild } from \"@/lib/react/remote\";\n\nexport interface LinkProps\n extends\n PropsWithChildren<Omit<Aria.LinkProps, \"children\" | \"slot\" | \"className\">>,\n FlowComponentProps<HTMLAnchorElement>,\n PropsWithClassName {\n /** Whether the link should be styled for being displayed inside a text. */\n inline?: boolean;\n /** An alternative link component. */\n linkComponent?: ComponentType<Omit<ComponentProps<\"a\">, \"ref\">>;\n /** The color of the link. @default \"default\" */\n color?: \"default\" | AlphaColor;\n /** Marks the link as the currently active one, e.g. in a navigation. */\n \"aria-current\"?: string;\n /**\n * The name of the slot the link is placed in. In action groups: `primary` (at\n * the end), `secondary` (before the primary action) or `abort` (at the\n * start).\n */\n slot?: string;\n /** The whiteSpace css value of the element. */\n whiteSpace?: CSSProperties[\"whiteSpace\"];\n /** The size of the element. @default \"m\" */\n size?: \"s\" | \"m\";\n /** @internal */\n unstyled?: boolean;\n}\n\n/** @flr-generate all */\nexport const Link = flowComponent(\"Link\", (props) => {\n const {\n children,\n className,\n inline,\n linkComponent: linkComponentFromProps,\n color = \"default\",\n unstyled = false,\n \"aria-current\": ariaCurrent,\n ref,\n slot: ignoredSlotProp,\n style,\n whiteSpace,\n size = \"m\",\n onClickCapture: onClickCaptureFromProps,\n target,\n download,\n ...rest\n } = props;\n\n const { linkComponent: linkComponentFromContext } = useContext(linkContext);\n const isSkeleton = useSkeletonMode();\n const Link = linkComponentFromProps\n ? (linkComponentFromProps as typeof Aria.Link)\n : props.href && linkComponentFromContext\n ? (linkComponentFromContext as typeof Aria.Link)\n : Aria.Link;\n\n const router = useRouter();\n\n /**\n * An interactive child (e.g. a Button) stops click propagation via React\n * Aria's `usePress`, so the anchor's own `onClick` never runs and navigation\n * falls back to a full page load. Handling it in the capture phase (top-down,\n * before the child) keeps navigation client-side. Only needed for React\n * Aria's `<a>`; custom link components handle navigation themselves.\n */\n const handleClickCapture =\n Link === Aria.Link && props.href\n ? (event: MouseEvent<HTMLAnchorElement>) => {\n onClickCaptureFromProps?.(event);\n if (event.target !== event.currentTarget) {\n handleLinkClick(event, router, props.href, props.routerOptions);\n }\n }\n : onClickCaptureFromProps;\n\n const rootClassName = unstyled\n ? className\n : clsx(\n styles.link,\n inline && styles.inline,\n isAlphaColor(color) && styles[color],\n size === \"s\" && styles[\"size-s\"],\n className,\n );\n\n const propsContext: PropsContext = {\n Icon: {\n tunnel: {\n component: \"Link\",\n id: \"icon\",\n },\n className: styles.icon,\n size: \"s\",\n },\n AlertText: {\n className: styles.alertText,\n },\n AccentBox: { className: styles.accentBox },\n LayoutCard: { className: styles.layoutCard },\n Button: {\n elementType: \"span\",\n className: styles.button,\n size: props.size,\n color: props.color === \"default\" ? undefined : props.color,\n isDisabled: props.isDisabled,\n children: dynamic((buttonProps) => (\n <>\n {buttonProps.children}\n <LinkIcon download={download} target={target} unstyled={unstyled} />\n </>\n )),\n },\n };\n\n const unsupportedTypingsLinkProps = ariaCurrent\n ? ({\n \"aria-current\": true,\n } as Record<string, unknown>)\n : {};\n\n /* Without text of its own — a Link styled as a Button, a Link around a\n LayoutCard — the content follows its own skeleton rules instead of\n becoming a text bar. */\n const hasOnlyElements = hasContent(children) && !containsTextChild(children);\n\n const textBars = (\n <SkeletonTextContent defaultWidth=\"6em\">{children}</SkeletonTextContent>\n );\n\n const content = (\n <PropsContextProvider props={propsContext}>\n {hasOnlyElements ? children : textBars}\n <LinkIcon\n withZeroWidthJoiner\n unstyled={unstyled}\n target={target}\n download={download}\n >\n <UiComponentTunnelExit id={\"icon\"} component={\"Link\"} />\n </LinkIcon>\n </PropsContextProvider>\n );\n\n /* react-aria's Link drops `inert`, and a skeleton needs no anchor. */\n if (isSkeleton) {\n return (\n <span className={rootClassName} style={{ ...style, whiteSpace }} inert>\n {content}\n </span>\n );\n }\n\n return (\n <Link\n {...unsupportedTypingsLinkProps}\n {...rest}\n target={target}\n download={download}\n onClickCapture={handleClickCapture}\n className={rootClassName}\n ref={ref}\n style={{ ...style, whiteSpace }}\n >\n {content}\n </Link>\n );\n});\n\nexport default Link;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAyDA,IAAa,OAAO,cAAc,SAAS,UAAU;CACnD,MAAM,EACJ,UACA,WACA,QACA,eAAe,wBACf,QAAQ,WACR,WAAW,OACX,gBAAgB,aAChB,KACA,MAAM,iBACN,OACA,YACA,OAAO,KACP,gBAAgB,yBAChB,QACA,UACA,GAAG,SACD;CAEJ,MAAM,EAAE,eAAe,6BAA6B,WAAW,WAAW;CAC1E,MAAM,aAAa,gBAAgB;CACnC,MAAM,OAAO,yBACR,yBACD,MAAM,QAAQ,2BACX,2BACD,KAAK;CAEX,MAAM,SAAS,UAAU;;;;;;;;CASzB,MAAM,qBACJ,SAAS,KAAK,QAAQ,MAAM,QACvB,UAAyC;EACxC,0BAA0B,KAAK;EAC/B,IAAI,MAAM,WAAW,MAAM,eACzB,gBAAgB,OAAO,QAAQ,MAAM,MAAM,MAAM,aAAa;CAElE,IACA;CAEN,MAAM,gBAAgB,WAClB,YACA,KACEA,oBAAO,MACP,UAAUA,oBAAO,QACjB,aAAa,KAAK,KAAKA,oBAAO,QAC9B,SAAS,OAAOA,oBAAO,WACvB,SACF;CAEJ,MAAM,eAA6B;EACjC,MAAM;GACJ,QAAQ;IACN,WAAW;IACX,IAAI;GACN;GACA,WAAWA,oBAAO;GAClB,MAAM;EACR;EACA,WAAW,EACT,WAAWA,oBAAO,UACpB;EACA,WAAW,EAAE,WAAWA,oBAAO,UAAU;EACzC,YAAY,EAAE,WAAWA,oBAAO,WAAW;EAC3C,QAAQ;GACN,aAAa;GACb,WAAWA,oBAAO;GAClB,MAAM,MAAM;GACZ,OAAO,MAAM,UAAU,YAAY,KAAA,IAAY,MAAM;GACrD,YAAY,MAAM;GAClB,UAAU,SAAS,gBACjB,qBAAAC,YAAA,EAAA,UAAA,CACG,YAAY,UACb,oBAAC,UAAD;IAAoB;IAAkB;IAAkB;GAAW,CAAA,CACnE,EAAA,CAAA,CACH;EACH;CACF;CAEA,MAAM,8BAA8B,cAC/B,EACC,gBAAgB,KAClB,IACA,CAAC;CAKL,MAAM,kBAAkB,WAAW,QAAQ,KAAK,CAAC,kBAAkB,QAAQ;CAM3E,MAAM,UACJ,qBAAC,sBAAD;EAAsB,OAAO;EAA7B,UAAA,CACG,kBAAkB,WALrBC,oBAAC,qBAAD;GAAqB,cAAa;GAAO;EAA8B,CAKvC,GAC9B,oBAAC,UAAD;GACE,qBAAA;GACU;GACF;GACE;GAEV,UAAA,oBAAC,uBAAD;IAAuB,IAAI;IAAQ,WAAW;GAAS,CAAA;EAC/C,CAAA,CACU;;CAIxB,IAAI,YACF,OACE,oBAAC,QAAD;EAAM,WAAW;EAAe,OAAO;GAAE,GAAG;GAAO;EAAW;EAAG,OAAA;EAC9D,UAAA;CACG,CAAA;CAIV,OACE,oBAAC,MAAD;EACE,GAAI;EACJ,GAAI;EACI;EACE;EACV,gBAAgB;EAChB,WAAW;EACN;EACL,OAAO;GAAE,GAAG;GAAO;EAAW;EAE7B,UAAA;CACG,CAAA;AAEV,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.module.mjs","names":[],"sources":["../../../../../../../src/components/Modal/Modal.module.scss"],"sourcesContent":["@use \"@/styles/mixins/scrollbar\";\n\n/* Caps the dialog width per size token (`size-s` / `size-m` / `size-l`). */\n@mixin size($size) {\n &.size-#{$size} {\n [role=\"dialog\"] {\n width: inherit;\n max-width: var(--modal--size--#{$size});\n }\n }\n}\n\n/* ===========================================================================\n Base — shared by Modal (centered / bottom sheet) and OffCanvas (side panel).\n =========================================================================== */\n.modal,\n.offCanvas {\n /* Overlay.module.scss lays the viewport out; the dialog inside it is the only\n pointer target. */\n > :global(.flow--overlay--viewport) {\n pointer-events: none;\n\n /* react-aria's own modal wrapper — it gets no className from Flow, so\n its default `react-aria-Modal` class is the handle. */\n > :global(.react-aria-Modal) {\n display: flex;\n justify-content: center;\n width: inherit;\n max-height: var(--visual-viewport-height);\n padding: var(--size-px--xl);\n }\n }\n\n /* Close button: hidden when an action group is present, unless forced. */\n &:has(.actionGroup) {\n .closeButton:not(.alwaysVisible) {\n display: none;\n }\n }\n\n .closeButton.alwaysHidden {\n display: none;\n }\n\n /* The dialog card itself — a flex column. It is also the scroll container on\n mobile, and the sticky header/footer stack against its content only. */\n [role=\"dialog\"] {\n display: flex;\n flex-direction: column;\n isolation: isolate;\n max-height: 100%;\n overflow: hidden;\n outline: none;\n pointer-events: all;\n background-color: var(--modal--background-color);\n border: var(--modal--border-width) var(--modal--border-style)\n var(--modal--border-color);\n\n // A consumer `Form` (or raw `<form>`) wrapping the modal content. `Form`\n // is a plain FC, so the props context cannot give it a class.\n // stylelint-disable-next-line selector-max-type -- consumer form, no class\n > form {\n display: contents;\n }\n }\n\n /* Header */\n // A `Header`/`SectionHeader` — or a raw `<header>` — the consumer places in\n // the modal; both must match.\n // stylelint-disable-next-line selector-max-type -- consumer-placed header\n header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .header {\n display: flex;\n align-items: center;\n gap: var(--heading--icon-to-text-spacing);\n padding-top: var(--modal--header-padding-top);\n padding-inline: var(--modal--padding);\n\n /* Flatten the heading's text and content spans into the header row, so\n badges stay behind the title instead of wrapping onto their own line.\n Heading's spans do carry classes, but `.flow--heading--heading-content`\n already sets `display: none` on `:empty` at the same specificity a class\n selector here would reach — source order would then decide. */\n // stylelint-disable-next-line selector-max-type -- see above\n > span {\n display: contents;\n }\n\n :global(.flow--heading--heading-text-spacer) {\n display: none;\n }\n\n :global(.flow--heading--heading-content)\n :global(.flow--heading--heading-content-item):not(:last-child) {\n margin-inline-end: 0;\n }\n\n .headerTitle {\n min-width: 0;\n }\n\n .closeButton {\n order: 1;\n margin-inline-start: auto;\n }\n }\n\n /* Content — the scrollable body */\n .content,\n .columnLayout {\n @include scrollbar.scrollbar;\n\n flex-grow: 1;\n min-height: var(--modal--content-min-height);\n padding: var(--modal--padding);\n overflow: hidden auto;\n background-color: var(--modal--background-color);\n }\n\n .columnLayout .accentBox {\n position: sticky;\n top: 0;\n height: fit-content;\n }\n\n /* Action group — the footer */\n .actionGroup {\n padding: var(--modal--padding);\n background-color: var(--modal--footer-background-color);\n }\n\n /* ActionGroup's \"sorted\" layout sets flex-grow:1 on itself; the modal footer\n must keep its natural height (otherwise it fills the full-height off-canvas\n panel). Scope via the dialog so flex-grow:0 wins the equal-specificity /\n source-order battle — without raising the specificity of the padding above,\n which the mobile rules override. */\n [role=\"dialog\"] .actionGroup {\n flex-grow: 0;\n }\n}\n\n/* ===========================================================================\n Modal — desktop (centered dialog, zoom in/out).\n =========================================================================== */\n@media (width > 768px) {\n .modal {\n @include size(s);\n @include size(m);\n @include size(l);\n\n [role=\"dialog\"] {\n border-radius: var(--modal--corner-radius);\n }\n\n > :global(.flow--overlay--viewport) > :global(.react-aria-Modal) {\n &[data-entering] {\n animation: modal-zoom var(--transition--duration--slow)\n cubic-bezier(0.1, 0.8, 0.3, 1.2);\n }\n\n &[data-exiting] {\n animation: modal-zoom var(--transition--duration--default)\n cubic-bezier(0.1, 0.8, 0.3, 1.2) reverse;\n }\n }\n }\n}\n\n/* ===========================================================================\n OffCanvas — side panel (all widths), full height, slides in from the side.\n =========================================================================== */\n.offCanvas {\n @include size(s);\n @include size(m);\n @include size(l);\n\n [role=\"dialog\"] {\n height: 100%;\n border-radius: 0;\n }\n\n > :global(.flow--overlay--viewport) {\n padding-inline-start: var(--size-px--m);\n\n > :global(.react-aria-Modal) {\n justify-content: flex-end;\n width: inherit;\n height: inherit;\n padding: unset;\n\n &[data-entering] {\n animation: modal-slide-in-from-right var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-in-from-right\n var(--transition--duration--default) reverse;\n }\n }\n }\n\n &.left {\n > :global(.flow--overlay--viewport) {\n padding-inline: 0 var(--size-px--m);\n\n > :global(.react-aria-Modal) {\n justify-content: flex-start;\n padding: unset;\n\n &[data-entering] {\n animation: modal-slide-in-from-left var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-in-from-left\n var(--transition--duration--default) reverse;\n }\n }\n }\n }\n}\n\n/* ===========================================================================\n Modal — mobile (bottom sheet), issue #2423.\n\n One scroll container: the dialog scrolls while the header sticks to the top\n and the footer to the bottom. While the virtual keyboard is open\n (.keyboardVisible) both un-stick so header + content + footer flow normally\n and the content gets the full available space.\n =========================================================================== */\n@media (max-width: 768px) {\n .modal {\n /* Bottom-anchored sheet: rounded top corners + slide-up animation. */\n > :global(.flow--overlay--viewport) > :global(.react-aria-Modal) {\n margin-top: auto;\n width: 100%;\n padding: unset;\n overflow: hidden;\n border-start-start-radius: var(--modal--corner-radius);\n border-start-end-radius: var(--modal--corner-radius);\n pointer-events: all;\n\n &[data-entering] {\n animation: modal-slide-up var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-up var(--transition--duration--default) reverse;\n }\n }\n\n /* The dialog is the single scroll container. Capped just below the visual\n viewport so a full-height sheet keeps a small gap from the top edge. */\n [role=\"dialog\"] {\n @include scrollbar.scrollbar;\n\n width: inherit;\n max-height: calc(\n var(--visual-viewport-height) - var(--modal--tray-top-spacing)\n );\n overflow: hidden auto;\n }\n\n // stylelint-disable-next-line selector-max-type -- consumer form, see above\n [role=\"dialog\"] > form {\n width: inherit;\n }\n\n /* flex: 0 0 auto keeps natural heights; without flex-shrink:0 the flex\n column compresses the content box and the (overflow:visible) fields\n spill over the footer. */\n .header,\n .content,\n .columnLayout,\n .actionGroup {\n flex: 0 0 auto;\n }\n\n .content,\n .columnLayout {\n min-height: 0;\n overflow: visible;\n }\n\n /* Docked header + footer (sticky within the single scroll container). */\n .header {\n position: sticky;\n top: 0;\n z-index: 1;\n background-color: var(--modal--background-color);\n }\n\n .actionGroup {\n position: sticky;\n bottom: 0;\n z-index: 1;\n }\n }\n\n /* Keyboard open: un-stick both so everything flows for maximum typing space.\n The dialog fills the available height and the footer uses margin-top:auto,\n so with SHORT content the footer still sits at the bottom (not mid-air) and\n with tall content it scrolls away with everything else. Never hidden. */\n .modal.keyboardVisible {\n [role=\"dialog\"] {\n min-height: calc(\n var(--visual-viewport-height) - var(--modal--tray-top-spacing)\n );\n }\n\n .header,\n .actionGroup {\n position: static;\n }\n\n .actionGroup {\n margin-top: auto;\n }\n }\n\n /* Shared mobile spacing + full-width stacked actions (Modal + OffCanvas). */\n .modal,\n .offCanvas {\n .header {\n padding-inline: var(--modal--padding-mobile);\n }\n\n .content,\n .columnLayout,\n .actionGroup {\n padding: var(--modal--padding-mobile);\n }\n\n .actionGroup {\n flex-direction: column;\n\n // The footer's buttons are consumer `Button`/`Action` children; a raw\n // `<button>` matches today too.\n // stylelint-disable-next-line selector-max-type -- consumer children\n button {\n width: 100%;\n }\n }\n }\n}\n\n/* ===========================================================================\n Animations\n =========================================================================== */\n@keyframes modal-zoom {\n /* Start with scale(1) to let ResizeObservers take the correct width on mount. */\n 0% {\n opacity: 0;\n transform: scale(1);\n }\n\n 0.1% {\n opacity: 1;\n transform: scale(0.8);\n }\n\n 100% {\n transform: scale(1);\n }\n}\n\n@keyframes modal-slide-up {\n from {\n translate: 0 30%;\n }\n\n to {\n translate: 0 0;\n }\n}\n\n@keyframes modal-slide-in-from-right {\n from {\n translate: 30% 0;\n }\n\n to {\n translate: 0 0;\n }\n}\n\n@keyframes modal-slide-in-from-left {\n from {\n translate: -30% 0;\n }\n\n to {\n translate: 0 0;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"Modal.module.mjs","names":[],"sources":["../../../../../../../src/components/Modal/Modal.module.scss"],"sourcesContent":["@use \"@/styles/mixins/scrollbar\";\n\n/* Caps the dialog width per size token (`size-s` / `size-m` / `size-l`). */\n@mixin size($size) {\n &.size-#{$size} {\n [role=\"dialog\"] {\n width: inherit;\n max-width: var(--modal--size--#{$size});\n }\n }\n}\n\n/* ===========================================================================\n Base — shared by Modal (centered / bottom sheet) and OffCanvas (side panel).\n =========================================================================== */\n.modal,\n.offCanvas {\n /* Overlay.module.scss lays the viewport out; the dialog inside it is the only\n pointer target. */\n > :global(.flow--overlay--viewport) {\n pointer-events: none;\n\n /* react-aria's own modal wrapper — it gets no className from Flow, so\n its default `react-aria-Modal` class is the handle. */\n > :global(.react-aria-Modal) {\n display: flex;\n justify-content: center;\n width: inherit;\n max-height: var(--visual-viewport-height);\n padding: var(--size-px--xl);\n }\n }\n\n /* Close button: hidden when an action group is present, unless forced. */\n &:has(.actionGroup) {\n .closeButton:not(.alwaysVisible) {\n display: none;\n }\n }\n\n .closeButton.alwaysHidden {\n display: none;\n }\n\n /* The dialog card itself — a flex column. It is also the scroll container on\n mobile, and the sticky header/footer stack against its content only. */\n [role=\"dialog\"] {\n display: flex;\n flex-direction: column;\n isolation: isolate;\n max-height: 100%;\n overflow: hidden;\n outline: none;\n pointer-events: all;\n background-color: var(--modal--background-color);\n border: var(--modal--border-width) var(--modal--border-style)\n var(--modal--border-color);\n\n // A consumer `Form` (or raw `<form>`) wrapping the modal content. `Form`\n // is a plain FC, so the props context cannot give it a class.\n // stylelint-disable-next-line selector-max-type -- consumer form, no class\n > form {\n display: contents;\n }\n }\n\n /* Header */\n // A `Header`/`SectionHeader` — or a raw `<header>` — the consumer places in\n // the modal; both must match.\n // stylelint-disable-next-line selector-max-type -- consumer-placed header\n header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .header {\n display: flex;\n align-items: center;\n gap: var(--heading--icon-to-text-spacing);\n padding-top: var(--modal--header-padding-top);\n padding-inline: var(--modal--padding);\n\n /* Flatten the heading's text and content spans into the header row, so\n badges stay behind the title instead of wrapping onto their own line.\n Heading's spans do carry classes, but `.flow--heading--heading-content`\n already sets `display: none` on `:empty` at the same specificity a class\n selector here would reach — source order would then decide. */\n // stylelint-disable-next-line selector-max-type -- see above\n > span {\n display: contents;\n }\n\n :global(.flow--heading--heading-text-spacer) {\n display: none;\n }\n\n :global(.flow--heading--heading-content)\n :global(.flow--heading--heading-content-item):not(:last-child) {\n margin-inline-end: 0;\n }\n\n .headerTitle {\n min-width: 0;\n }\n\n /* Remote apps built against Flow < 1.2.0 send the close button without\n ClearPropsContext, so the Heading tunnels it into a content item — that\n item is the flex item then — and passes it its button size `s`. */\n .closeButton,\n :global(.flow--heading--heading-content-item):has(.closeButton) {\n order: 1;\n margin-inline-start: auto;\n }\n\n .closeButton:global(.flow--button--size-s) {\n padding: var(--button--padding-icon-only);\n border-radius: var(--button--corner-radius);\n\n :global(.flow--icon) {\n width: var(--icon--size--m);\n height: var(--icon--size--m);\n }\n }\n }\n\n /* Content — the scrollable body */\n .content,\n .columnLayout {\n @include scrollbar.scrollbar;\n\n flex-grow: 1;\n min-height: var(--modal--content-min-height);\n padding: var(--modal--padding);\n overflow: hidden auto;\n background-color: var(--modal--background-color);\n }\n\n .columnLayout .accentBox {\n position: sticky;\n top: 0;\n height: fit-content;\n }\n\n /* Action group — the footer */\n .actionGroup {\n padding: var(--modal--padding);\n background-color: var(--modal--footer-background-color);\n }\n\n /* ActionGroup's \"sorted\" layout sets flex-grow:1 on itself; the modal footer\n must keep its natural height (otherwise it fills the full-height off-canvas\n panel). Scope via the dialog so flex-grow:0 wins the equal-specificity /\n source-order battle — without raising the specificity of the padding above,\n which the mobile rules override. */\n [role=\"dialog\"] .actionGroup {\n flex-grow: 0;\n }\n}\n\n/* ===========================================================================\n Modal — desktop (centered dialog, zoom in/out).\n =========================================================================== */\n@media (width > 768px) {\n .modal {\n @include size(s);\n @include size(m);\n @include size(l);\n\n [role=\"dialog\"] {\n border-radius: var(--modal--corner-radius);\n }\n\n > :global(.flow--overlay--viewport) > :global(.react-aria-Modal) {\n &[data-entering] {\n animation: modal-zoom var(--transition--duration--slow)\n cubic-bezier(0.1, 0.8, 0.3, 1.2);\n }\n\n &[data-exiting] {\n animation: modal-zoom var(--transition--duration--default)\n cubic-bezier(0.1, 0.8, 0.3, 1.2) reverse;\n }\n }\n }\n}\n\n/* ===========================================================================\n OffCanvas — side panel (all widths), full height, slides in from the side.\n =========================================================================== */\n.offCanvas {\n @include size(s);\n @include size(m);\n @include size(l);\n\n [role=\"dialog\"] {\n height: 100%;\n border-radius: 0;\n }\n\n > :global(.flow--overlay--viewport) {\n padding-inline-start: var(--size-px--m);\n\n > :global(.react-aria-Modal) {\n justify-content: flex-end;\n width: inherit;\n height: inherit;\n padding: unset;\n\n &[data-entering] {\n animation: modal-slide-in-from-right var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-in-from-right\n var(--transition--duration--default) reverse;\n }\n }\n }\n\n &.left {\n > :global(.flow--overlay--viewport) {\n padding-inline: 0 var(--size-px--m);\n\n > :global(.react-aria-Modal) {\n justify-content: flex-start;\n padding: unset;\n\n &[data-entering] {\n animation: modal-slide-in-from-left var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-in-from-left\n var(--transition--duration--default) reverse;\n }\n }\n }\n }\n}\n\n/* ===========================================================================\n Modal — mobile (bottom sheet), issue #2423.\n\n One scroll container: the dialog scrolls while the header sticks to the top\n and the footer to the bottom. While the virtual keyboard is open\n (.keyboardVisible) both un-stick so header + content + footer flow normally\n and the content gets the full available space.\n =========================================================================== */\n@media (max-width: 768px) {\n .modal {\n /* Bottom-anchored sheet: rounded top corners + slide-up animation. */\n > :global(.flow--overlay--viewport) > :global(.react-aria-Modal) {\n margin-top: auto;\n width: 100%;\n padding: unset;\n overflow: hidden;\n border-start-start-radius: var(--modal--corner-radius);\n border-start-end-radius: var(--modal--corner-radius);\n pointer-events: all;\n\n &[data-entering] {\n animation: modal-slide-up var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-up var(--transition--duration--default) reverse;\n }\n }\n\n /* The dialog is the single scroll container. Capped just below the visual\n viewport so a full-height sheet keeps a small gap from the top edge. */\n [role=\"dialog\"] {\n @include scrollbar.scrollbar;\n\n width: inherit;\n max-height: calc(\n var(--visual-viewport-height) - var(--modal--tray-top-spacing)\n );\n overflow: hidden auto;\n }\n\n // stylelint-disable-next-line selector-max-type -- consumer form, see above\n [role=\"dialog\"] > form {\n width: inherit;\n }\n\n /* flex: 0 0 auto keeps natural heights; without flex-shrink:0 the flex\n column compresses the content box and the (overflow:visible) fields\n spill over the footer. */\n .header,\n .content,\n .columnLayout,\n .actionGroup {\n flex: 0 0 auto;\n }\n\n .content,\n .columnLayout {\n min-height: 0;\n overflow: visible;\n }\n\n /* Docked header + footer (sticky within the single scroll container). */\n .header {\n position: sticky;\n top: 0;\n z-index: 1;\n background-color: var(--modal--background-color);\n }\n\n .actionGroup {\n position: sticky;\n bottom: 0;\n z-index: 1;\n }\n }\n\n /* Keyboard open: un-stick both so everything flows for maximum typing space.\n The dialog fills the available height and the footer uses margin-top:auto,\n so with SHORT content the footer still sits at the bottom (not mid-air) and\n with tall content it scrolls away with everything else. Never hidden. */\n .modal.keyboardVisible {\n [role=\"dialog\"] {\n min-height: calc(\n var(--visual-viewport-height) - var(--modal--tray-top-spacing)\n );\n }\n\n .header,\n .actionGroup {\n position: static;\n }\n\n .actionGroup {\n margin-top: auto;\n }\n }\n\n /* Shared mobile spacing + full-width stacked actions (Modal + OffCanvas). */\n .modal,\n .offCanvas {\n .header {\n padding-inline: var(--modal--padding-mobile);\n }\n\n .content,\n .columnLayout,\n .actionGroup {\n padding: var(--modal--padding-mobile);\n }\n\n .actionGroup {\n flex-direction: column;\n\n // The footer's buttons are consumer `Button`/`Action` children; a raw\n // `<button>` matches today too.\n // stylelint-disable-next-line selector-max-type -- consumer children\n button {\n width: 100%;\n }\n }\n }\n}\n\n/* ===========================================================================\n Animations\n =========================================================================== */\n@keyframes modal-zoom {\n /* Start with scale(1) to let ResizeObservers take the correct width on mount. */\n 0% {\n opacity: 0;\n transform: scale(1);\n }\n\n 0.1% {\n opacity: 1;\n transform: scale(0.8);\n }\n\n 100% {\n transform: scale(1);\n }\n}\n\n@keyframes modal-slide-up {\n from {\n translate: 0 30%;\n }\n\n to {\n translate: 0 0;\n }\n}\n\n@keyframes modal-slide-in-from-right {\n from {\n translate: 30% 0;\n }\n\n to {\n translate: 0 0;\n }\n}\n\n@keyframes modal-slide-in-from-left {\n from {\n translate: -30% 0;\n }\n\n to {\n translate: 0 0;\n }\n}\n"],"mappings":""}
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
  /* */
3
- import { useLocalizedStringFormatter } from "../TranslationProvider/useLocalizedStringFormatter.mjs";
4
3
  import { skeletonModeContext } from "./skeletonModeContext.mjs";
4
+ import { useLocalizedStringFormatter } from "../TranslationProvider/useLocalizedStringFormatter.mjs";
5
5
  import __locale_default from "../../../../../_virtual/_32.locale.mjs";
6
6
  import { useContext } from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
  /* */
3
- import { getTextOfChild, isTextChild } from "../../lib/isTextChild.mjs";
4
3
  import { useSkeletonMode } from "../../skeletonModeContext.mjs";
4
+ import { getTextOfChild, isTextChild } from "../../lib/isTextChild.mjs";
5
5
  import { SkeletonTextContent } from "../SkeletonTextContent/SkeletonTextContent.mjs";
6
6
  import { Children } from "react";
7
7
  import { jsx } from "react/jsx-runtime";
@@ -1,8 +1,8 @@
1
1
  "use client"
2
2
  /* */
3
+ import { SkeletonModeReset, useSkeletonMode } from "../../skeletonModeContext.mjs";
3
4
  import { SkeletonText } from "../../../SkeletonText/SkeletonText.mjs";
4
5
  import { hasContent } from "../../lib/hasContent.mjs";
5
- import { SkeletonModeReset, useSkeletonMode } from "../../skeletonModeContext.mjs";
6
6
  import SkeletonTextContent_module_default from "./SkeletonTextContent.module.mjs";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  //#region src/components/SkeletonMode/components/SkeletonTextContent/SkeletonTextContent.tsx
@@ -3,10 +3,10 @@
3
3
  import { PropsContextProvider } from "../../lib/propsContext/components/PropsContextProvider.mjs";
4
4
  import { flowComponent } from "../../lib/componentFactory/flowComponent.mjs";
5
5
  import { isAlphaColor } from "../../lib/types/props.mjs";
6
+ import { useSkeletonMode } from "../SkeletonMode/skeletonModeContext.mjs";
6
7
  import { Wrap } from "../Wrap/Wrap.mjs";
7
8
  import { EmulatedBoldText } from "../EmulatedBoldText/EmulatedBoldText.mjs";
8
9
  import Text_module_default from "./Text.module.mjs";
9
- import { useSkeletonMode } from "../SkeletonMode/skeletonModeContext.mjs";
10
10
  import { SkeletonTextContent } from "../SkeletonMode/components/SkeletonTextContent/SkeletonTextContent.mjs";
11
11
  import clsx from "clsx";
12
12
  import { jsx } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/flow-react-components",
3
- "version": "1.5.0-next.35",
3
+ "version": "1.5.0-next.37",
4
4
  "type": "module",
5
5
  "description": "A React implementation of Flow, mittwald’s design system",
6
6
  "homepage": "https://flow.mittwald.de",
@@ -84,9 +84,9 @@
84
84
  "@internationalized/string-compiler": "^3.4.1",
85
85
  "@lezer/common": "^1.5.2",
86
86
  "@lezer/highlight": "^1.2.3",
87
- "@mittwald/flow-icons": "^1.5.0-next.35",
87
+ "@mittwald/flow-icons": "^1.5.0-next.37",
88
88
  "@mittwald/password-tools-js": "^3.0.0",
89
- "@mittwald/react-tunnel": "^1.5.0-next.35",
89
+ "@mittwald/react-tunnel": "^1.5.0-next.37",
90
90
  "@mittwald/react-use-promise": "^4.2.2",
91
91
  "@react-aria/form": "^3.2.1",
92
92
  "@react-aria/i18n": "^3.13.1",
@@ -139,12 +139,12 @@
139
139
  "@internationalized/date": "^3.12.3",
140
140
  "@lezer/generator": "^1.8.0",
141
141
  "@lezer/lr": "^1.4.10",
142
- "@mittwald/flow-core": "1.5.0-next.35",
143
- "@mittwald/flow-design-tokens": "1.5.0-next.35",
144
- "@mittwald/flow-icons-base": "1.5.0-next.35",
142
+ "@mittwald/flow-core": "1.5.0-next.37",
143
+ "@mittwald/flow-design-tokens": "1.5.0-next.37",
144
+ "@mittwald/flow-icons-base": "1.5.0-next.37",
145
145
  "@mittwald/react-use-promise": "^4.2.2",
146
146
  "@mittwald/remote-dom-react": "1.2.2-mittwald.10",
147
- "@mittwald/typescript-config": "1.5.0-next.35",
147
+ "@mittwald/typescript-config": "1.5.0-next.37",
148
148
  "@nx/storybook": "^22.7.6",
149
149
  "@storybook/addon-a11y": "^10.5.10",
150
150
  "@storybook/addon-actions": "^9.0.8",
@@ -198,7 +198,7 @@
198
198
  },
199
199
  "peerDependencies": {
200
200
  "@internationalized/date": "^3.12.2",
201
- "@mittwald/flow-icons-pro": "1.5.0-next.35",
201
+ "@mittwald/flow-icons-pro": "1.5.0-next.37",
202
202
  "@mittwald/react-use-promise": "^4.2.2",
203
203
  "next": "^16.2.3",
204
204
  "react": "^19.2.0",
@@ -222,5 +222,5 @@
222
222
  "browserslist": [
223
223
  "baseline widely available"
224
224
  ],
225
- "gitHead": "9ecbf1107cfba989f9c248eacb47b5465ed2e6c2"
225
+ "gitHead": "55a70fecdd275e5beca4858407e6093e050105d0"
226
226
  }