@12-apps/ui 6.39.11 → 6.39.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/_internal/components/form/RichTextEditor/RichTextEditor.hooks.js +1 -1
  2. package/dist/_internal/components/form/RichTextEditor/RichTextEditor.js +2 -2
  3. package/dist/_internal/components/form/index.js +2 -2
  4. package/dist/_internal/components/index.js +4 -4
  5. package/dist/_internal/components/layout/Separator/Separator.js +2 -2
  6. package/dist/_internal/components/layout/Separator/Separator.styles.js +1 -1
  7. package/dist/_internal/components/layout/index.js +2 -2
  8. package/dist/_internal/social-login-button/social-login-button.js +3 -3
  9. package/dist/{chunk-SYNB3IIB.js → chunk-35443PF6.js} +8 -5
  10. package/dist/chunk-35443PF6.js.map +1 -0
  11. package/dist/{chunk-774B27QA.js → chunk-EUACQR6N.js} +4 -3
  12. package/dist/chunk-EUACQR6N.js.map +1 -0
  13. package/dist/{chunk-7EM4THID.js → chunk-RTEY5COP.js} +2 -2
  14. package/dist/{chunk-5KWHINO5.js → chunk-SLDSWQVE.js} +2 -2
  15. package/dist/{chunk-XSSCSRUX.js → chunk-X53F33KA.js} +4 -2
  16. package/dist/chunk-X53F33KA.js.map +1 -0
  17. package/dist/form/RichTextEditor.js +2 -2
  18. package/dist/layout/Separator.js +2 -2
  19. package/dist/social-login-button.js +3 -3
  20. package/dist/types/components/form/RichTextEditor/RichTextEditor.hooks.d.ts.map +1 -1
  21. package/dist/types/components/layout/Separator/Separator.d.ts.map +1 -1
  22. package/dist/types/components/layout/Separator/Separator.styles.d.ts +12 -4
  23. package/dist/types/components/layout/Separator/Separator.styles.d.ts.map +1 -1
  24. package/package.json +1 -1
  25. package/src/components/form/RichTextEditor/RichTextEditor.hooks.ts +20 -3
  26. package/src/components/layout/Separator/Separator.styles.ts +26 -8
  27. package/src/components/layout/Separator/Separator.tsx +6 -0
  28. package/dist/chunk-774B27QA.js.map +0 -1
  29. package/dist/chunk-SYNB3IIB.js.map +0 -1
  30. package/dist/chunk-XSSCSRUX.js.map +0 -1
  31. /package/dist/{chunk-7EM4THID.js.map → chunk-RTEY5COP.js.map} +0 -0
  32. /package/dist/{chunk-5KWHINO5.js.map → chunk-SLDSWQVE.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useRichTextEditor
3
- } from "../../../../chunk-774B27QA.js";
3
+ } from "../../../../chunk-EUACQR6N.js";
4
4
  import "../../../../chunk-UZOG6HNN.js";
5
5
  import "../../../../chunk-33LAPBYB.js";
6
6
  import "../../../../chunk-PZ5AY32C.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  RichTextEditor,
3
3
  RichTextEditor_default
4
- } from "../../../../chunk-7EM4THID.js";
5
- import "../../../../chunk-774B27QA.js";
4
+ } from "../../../../chunk-RTEY5COP.js";
5
+ import "../../../../chunk-EUACQR6N.js";
6
6
  import "../../../../chunk-UZOG6HNN.js";
7
7
  import "../../../../chunk-33LAPBYB.js";
8
8
  import "../../../../chunk-WVZBYTIK.js";
@@ -8,8 +8,8 @@ import "../../../chunk-A5UYPUS2.js";
8
8
  import "../../../chunk-WMY276HD.js";
9
9
  import {
10
10
  RichTextEditor
11
- } from "../../../chunk-7EM4THID.js";
12
- import "../../../chunk-774B27QA.js";
11
+ } from "../../../chunk-RTEY5COP.js";
12
+ import "../../../chunk-EUACQR6N.js";
13
13
  import "../../../chunk-UZOG6HNN.js";
14
14
  import "../../../chunk-33LAPBYB.js";
15
15
  import "../../../chunk-WVZBYTIK.js";
@@ -196,8 +196,8 @@ import "../../chunk-YBVKLJAL.js";
196
196
  import "../../chunk-WMY276HD.js";
197
197
  import {
198
198
  RichTextEditor
199
- } from "../../chunk-7EM4THID.js";
200
- import "../../chunk-774B27QA.js";
199
+ } from "../../chunk-RTEY5COP.js";
200
+ import "../../chunk-EUACQR6N.js";
201
201
  import "../../chunk-UZOG6HNN.js";
202
202
  import "../../chunk-33LAPBYB.js";
203
203
  import "../../chunk-WVZBYTIK.js";
@@ -768,8 +768,8 @@ import "../../chunk-CYZZHWNK.js";
768
768
  import "../../chunk-CMMPGAMJ.js";
769
769
  import {
770
770
  Separator
771
- } from "../../chunk-XSSCSRUX.js";
772
- import "../../chunk-SYNB3IIB.js";
771
+ } from "../../chunk-X53F33KA.js";
772
+ import "../../chunk-35443PF6.js";
773
773
  import {
774
774
  Avatar,
775
775
  AvatarGroup
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Separator
3
- } from "../../../../chunk-XSSCSRUX.js";
4
- import "../../../../chunk-SYNB3IIB.js";
3
+ } from "../../../../chunk-X53F33KA.js";
4
+ import "../../../../chunk-35443PF6.js";
5
5
  import "../../../../chunk-PZBK3F52.js";
6
6
  import "../../../../chunk-YKCAJ2TN.js";
7
7
  import "../../../../chunk-PZ5AY32C.js";
@@ -3,7 +3,7 @@ import {
3
3
  separatorBlockMargin,
4
4
  separatorMargin,
5
5
  separatorStyles
6
- } from "../../../../chunk-SYNB3IIB.js";
6
+ } from "../../../../chunk-35443PF6.js";
7
7
  import "../../../../chunk-PZBK3F52.js";
8
8
  import "../../../../chunk-YKCAJ2TN.js";
9
9
  import "../../../../chunk-PZ5AY32C.js";
@@ -103,8 +103,8 @@ import "../../../chunk-7WDPC46N.js";
103
103
  import "../../../chunk-CMMPGAMJ.js";
104
104
  import {
105
105
  Separator
106
- } from "../../../chunk-XSSCSRUX.js";
107
- import "../../../chunk-SYNB3IIB.js";
106
+ } from "../../../chunk-X53F33KA.js";
107
+ import "../../../chunk-35443PF6.js";
108
108
  import "../../../chunk-2GSIEDGN.js";
109
109
  import {
110
110
  Card,
@@ -5,15 +5,15 @@ import {
5
5
  GoogleIcon,
6
6
  SocialLoginButton,
7
7
  SocialLoginContainer
8
- } from "../../chunk-5KWHINO5.js";
8
+ } from "../../chunk-SLDSWQVE.js";
9
9
  import "../../chunk-INGPH3YQ.js";
10
10
  import "../../chunk-6GP2DOOO.js";
11
11
  import "../../chunk-IWEUSZY3.js";
12
12
  import "../../chunk-RB3X6CEE.js";
13
13
  import "../../chunk-CYZZHWNK.js";
14
14
  import "../../chunk-CMMPGAMJ.js";
15
- import "../../chunk-XSSCSRUX.js";
16
- import "../../chunk-SYNB3IIB.js";
15
+ import "../../chunk-X53F33KA.js";
16
+ import "../../chunk-35443PF6.js";
17
17
  import "../../chunk-VUHLNYAE.js";
18
18
  import "../../chunk-2GSIEDGN.js";
19
19
  import "../../chunk-WN5PNP6F.js";
@@ -70,13 +70,16 @@ var ruleLength = (theme, length) => {
70
70
  if (typeof length === "string") return length;
71
71
  return length <= 1 ? `${length * 100}%` : rem(theme, length);
72
72
  };
73
+ var isRelativeLength = (length) => typeof length === "number" ? length <= 1 : length.includes("%");
73
74
  var labelledSeparatorLayout = (theme, isHorizontal, length) => {
74
75
  if (isHorizontal) return { rule: {}, group: {} };
75
- if (length) return { rule: { flex: "none" }, group: {} };
76
+ if (length && !isRelativeLength(length)) return { rule: { flex: "none" }, group: {} };
76
77
  return {
77
- // A design 16px, as long as the gap beside the label at the default
78
- // spacing, so a short or unsized row still shows two rules and not a word.
79
- rule: { flex: "1 1 0", minHeight: rem(theme, 16) },
78
+ // Unset, the rules share the run; relative, each is its own fraction of it.
79
+ // Either way a design 16px floor, as long as the gap beside the label at
80
+ // the default spacing, so a short or unsized row still shows two rules and
81
+ // not a word.
82
+ rule: { flex: length ? "0 1 auto" : "1 1 0", minHeight: rem(theme, 16) },
80
83
  group: { alignSelf: "stretch" }
81
84
  };
82
85
  };
@@ -113,4 +116,4 @@ export {
113
116
  labelledSeparatorLayout,
114
117
  separatorStyles
115
118
  };
116
- //# sourceMappingURL=chunk-SYNB3IIB.js.map
119
+ //# sourceMappingURL=chunk-35443PF6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/layout/Separator/Separator.styles.ts"],"sourcesContent":["import type { CSSObject, Theme } from '@mui/material/styles/index.js';\n\nimport type {\n SeparatorOrientation,\n SeparatorProps,\n SeparatorSize,\n SeparatorVariant,\n} from './Separator.types';\nimport { FIELD_BORDER_WIDTH } from '../../../tokens/field-height.core';\nimport { rem } from '../../../tokens/relative';\n\nconst THICKNESS_PX: Record<SeparatorSize, number> = {\n xs: 1,\n sm: 2,\n md: 3,\n lg: 4,\n xl: 6,\n};\n\nconst MARGIN_PX: Record<SeparatorSize, number> = {\n xs: 4,\n sm: 8,\n md: 16,\n lg: 24,\n xl: 32,\n};\n\nconst separatorThickness = (size: SeparatorSize): number =>\n THICKNESS_PX[size] ?? THICKNESS_PX.md;\n\n/** An explicit `margin` wins; otherwise the size picks one. */\nexport const separatorMargin = (\n size: SeparatorSize,\n margin?: number | string,\n): number | string => margin ?? MARGIN_PX[size] ?? MARGIN_PX.md;\n\n// `gradient` paints a background rather than a border, so it draws its line as a\n// solid border of zero visual consequence.\nconst borderStyleFor = (variant: SeparatorVariant): 'dashed' | 'dotted' | 'solid' => {\n switch (variant) {\n case 'dashed':\n return 'dashed';\n case 'dotted':\n return 'dotted';\n default:\n return 'solid';\n }\n};\n\nconst gradientBackground = (\n variant: SeparatorVariant,\n orientation: SeparatorOrientation,\n color: string,\n): string | undefined => {\n if (variant !== 'gradient') return undefined;\n const angle = orientation === 'horizontal' ? '90deg' : '180deg';\n return `linear-gradient(${angle}, transparent 0%, ${color} 50%, transparent 100%)`;\n};\n\n// Only the leading edge carries the rule; the other three are zeroed explicitly so\n// a parent's border shorthand cannot leak through.\nconst borderEdges = (\n isHorizontal: boolean,\n thickness: string,\n style: 'dashed' | 'dotted' | 'solid',\n color: string,\n): CSSObject => {\n const leading = isHorizontal ? 'Top' : 'Left';\n const blank: CSSObject = {\n borderTopWidth: 0,\n borderBottomWidth: 0,\n borderLeftWidth: 0,\n borderRightWidth: 0,\n borderTopStyle: 'none',\n borderBottomStyle: 'none',\n borderLeftStyle: 'none',\n borderRightStyle: 'none',\n borderTopColor: 'transparent',\n borderBottomColor: 'transparent',\n borderLeftColor: 'transparent',\n borderRightColor: 'transparent',\n };\n\n return {\n ...blank,\n [`border${leading}Width`]: thickness,\n [`border${leading}Style`]: style,\n [`border${leading}Color`]: color,\n };\n};\n\ntype SeparatorStyleArgs = Required<\n Pick<SeparatorProps, 'variant' | 'orientation' | 'size'>\n> &\n Pick<SeparatorProps, 'color' | 'length'> & {\n /** From {@link separatorBlockMargin}, which both the plain and the labelled separator read. */\n blockMargin: string;\n };\n\n/**\n * The separator's margin, on the axis it separates along only: above and below\n * a horizontal rule, either side of a vertical one. A margin given as a string\n * carries its own units; a number is design px, through the type scale.\n *\n * The LABELLED separator uses this too. It used to hand the number straight to\n * `sx.margin`, where a number is SPACING UNITS — `md`'s 16 drew 128px on all\n * four sides, against 16px above and below for the unlabelled one.\n */\nexport const separatorBlockMargin = (\n theme: Theme,\n size: SeparatorSize,\n margin: number | string | undefined,\n isHorizontal: boolean,\n): string => {\n const value = separatorMargin(size, margin);\n const length = typeof value === 'string' ? value : rem(theme, value);\n return isHorizontal ? `${length} 0` : `0 ${length}`;\n};\n\n/**\n * The rule's extent along its axis. It lands in `sx`, which reads a number of 1\n * or less as a fraction of the parent, and that stays so; any other number is\n * design px, through the type scale. Unset (or `0`/`''`) is the full run.\n */\nconst ruleLength = (theme: Theme, length: SeparatorProps['length']): string => {\n if (!length) return '100%';\n if (typeof length === 'string') return length;\n return length <= 1 ? `${length * 100}%` : rem(theme, length);\n};\n\n/**\n * Whether a set `length` is a fraction of the parent rather than a fixed\n * extent: a number of 1 or less, as {@link ruleLength} reads it, or any string\n * that carries a percentage (`'50%'`, `'calc(50% - 8px)'`).\n */\nconst isRelativeLength = (length: number | string): boolean =>\n typeof length === 'number' ? length <= 1 : length.includes('%');\n\n/**\n * What a labelled separator's rules add to {@link separatorStyles}, and whether\n * the group stretches to its parent (FUT-2617, FUT-2675).\n *\n * A labelled VERTICAL separator is a flex COLUMN — rule, label, rule — and with\n * no `length` each rule is `height: 100%` of a column whose height is its own\n * content: both resolved to 0px and only the label showed. So the column\n * stretches to the row it sits in and the two rules split what the label\n * leaves, equally (`flex: 1 1 0`), never shorter than a visible minimum.\n *\n * A RELATIVE `length` (FUT-2675) resolved against that same content-height\n * column and drew 0px too. It now means what it means on a horizontal labelled\n * separator: that fraction of the run. The column stretches as above and each\n * rule is `length` of it, with no grow and the default shrink, so rule, label\n * and rule never overflow the row; the same minimum holds, and is all a block\n * parent (where `align-self` does nothing) gets.\n *\n * An ABSOLUTE `length` is exactly that long: no grow, no shrink, no basis, and\n * the column keeps its content height, centred as before.\n *\n * Only the labelled branch reads this. The plain separator keeps\n * `separatorStyles` as is, since `flex: 1 1 0` there would widen a vertical\n * separator sitting in a row. The horizontal rules already shrink around the\n * label in their row, so they are left alone.\n */\nexport const labelledSeparatorLayout = (\n theme: Theme,\n isHorizontal: boolean,\n length: SeparatorProps['length'],\n): { rule: CSSObject; group: CSSObject } => {\n if (isHorizontal) return { rule: {}, group: {} };\n if (length && !isRelativeLength(length)) return { rule: { flex: 'none' }, group: {} };\n return {\n // Unset, the rules share the run; relative, each is its own fraction of it.\n // Either way a design 16px floor, as long as the gap beside the label at\n // the default spacing, so a short or unsized row still shows two rules and\n // not a word.\n rule: { flex: length ? '0 1 auto' : '1 1 0', minHeight: rem(theme, 16) },\n group: { alignSelf: 'stretch' },\n };\n};\n\nexport const separatorStyles = (\n theme: Theme,\n { variant, orientation, size, color, blockMargin, length }: SeparatorStyleArgs,\n): CSSObject => {\n const isHorizontal = orientation === 'horizontal';\n // `xs` is the 1px hairline: it stays one device-independent pixel at every\n // root size, like every other hairline in the package; heavier rules scale.\n const thicknessPx = separatorThickness(size);\n const thickness = thicknessPx === FIELD_BORDER_WIDTH ? `${FIELD_BORDER_WIDTH}px` : rem(theme, thicknessPx);\n const along = ruleLength(theme, length);\n const resolvedColor = color || theme.palette.divider;\n\n const baseStyles: CSSObject = {\n display: 'flex',\n alignItems: 'center',\n margin: blockMargin,\n width: isHorizontal ? along : thickness,\n height: isHorizontal ? thickness : along,\n boxSizing: 'border-box',\n };\n\n if (variant === 'gradient') {\n return {\n ...baseStyles,\n background: gradientBackground(variant, orientation, resolvedColor),\n };\n }\n\n return {\n ...baseStyles,\n backgroundColor: 'transparent',\n ...borderEdges(isHorizontal, thickness, borderStyleFor(variant), resolvedColor),\n };\n};\n"],"mappings":"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","names":[]}
@@ -34,6 +34,7 @@ var SANITIZE_CONFIG = {
34
34
  ALLOW_DATA_ATTR: false,
35
35
  KEEP_CONTENT: true
36
36
  };
37
+ var sanitize = (html) => DOMPurify.sanitize(html, SANITIZE_CONFIG);
37
38
  var textLengthOf = (html) => {
38
39
  const probe = document.createElement("div");
39
40
  probe.innerHTML = html;
@@ -50,7 +51,7 @@ var useEditorContent = ({
50
51
  if (!editorRef.current) return;
51
52
  const newContent = editorRef.current.innerHTML;
52
53
  if (maxLength && (editorRef.current.textContent || "").length > maxLength) {
53
- editorRef.current.innerHTML = content;
54
+ editorRef.current.innerHTML = sanitize(content);
54
55
  return;
55
56
  }
56
57
  setContent(newContent);
@@ -106,7 +107,7 @@ var useRichTextEditor = ({
106
107
  },
107
108
  [disabled, readOnly, handleContentChange]
108
109
  );
109
- const sanitizedContent = DOMPurify.sanitize(content, SANITIZE_CONFIG);
110
+ const sanitizedContent = sanitize(content);
110
111
  useEffect(() => {
111
112
  if (editorRef.current && editorRef.current.innerHTML !== sanitizedContent) {
112
113
  editorRef.current.innerHTML = sanitizedContent;
@@ -126,4 +127,4 @@ var useRichTextEditor = ({
126
127
  export {
127
128
  useRichTextEditor
128
129
  };
129
- //# sourceMappingURL=chunk-774B27QA.js.map
130
+ //# sourceMappingURL=chunk-EUACQR6N.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/form/RichTextEditor/RichTextEditor.hooks.ts"],"sourcesContent":["import DOMPurify from 'dompurify';\nimport type React from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { applyFormatToRange } from './RichTextEditor.format';\n\n// Deliberately narrow: anything not listed is stripped, and no data-* attributes\n// survive, so pasted markup cannot smuggle in behaviour.\nconst SANITIZE_CONFIG = {\n ALLOWED_TAGS: [\n 'p', 'br', 'strong', 'em', 'u', 's', 'a', 'img', 'ul', 'ol', 'li',\n 'blockquote', 'pre', 'code', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'div',\n ],\n ALLOWED_ATTR: ['href', 'src', 'alt', 'target', 'rel', 'style', 'class'],\n ALLOW_DATA_ATTR: false,\n KEEP_CONTENT: true,\n};\n\n// The one way editor html reaches the DOM. Both writes (the render-path effect\n// and the over-limit restore) use it, so they cannot drift to different configs.\nconst sanitize = (html: string): string => DOMPurify.sanitize(html, SANITIZE_CONFIG);\n\n// INVARIANT: only ever pass SANITISED html here. Assigning innerHTML to a\n// detached element still fetches `<img src>` and fires its `onerror`, so raw\n// markup would execute. Its one caller passes `sanitizedContent`.\nconst textLengthOf = (html: string): number => {\n const probe = document.createElement('div');\n probe.innerHTML = html;\n return probe.textContent?.length || 0;\n};\n\n// The editable content and the limit that guards it.\nconst useEditorContent = ({\n editorRef,\n value,\n onChange,\n maxLength,\n}: {\n editorRef: React.RefObject<HTMLDivElement | null>;\n value?: string;\n onChange?: (value: string) => void;\n maxLength?: number;\n}) => {\n // `content` holds UNSANITISED html: the caller's raw `value` (read once, at\n // mount) until the first accepted input, and the editor's raw live innerHTML\n // after it. Every write of it to the DOM must go through `sanitize` below.\n // INVARIANT: if syncing a later `value` is ever added, the new value reaches\n // the DOM through the same sanitiser, never by a direct innerHTML write.\n const [content, setContent] = useState(value ?? '');\n\n const handleContentChange = useCallback(() => {\n if (!editorRef.current) return;\n\n const newContent = editorRef.current.innerHTML;\n\n // Over the limit, put the last accepted content back rather than truncating\n // mid-markup. `content` may be the caller's raw `value`, so the restore is\n // sanitised exactly as the render path is: writing it raw ran an `onerror`\n // handler carried in `value` (FUT-2685).\n if (maxLength && (editorRef.current.textContent || '').length > maxLength) {\n editorRef.current.innerHTML = sanitize(content);\n return;\n }\n\n // Emitting the live DOM is safe only because nothing but `sanitize()` ever\n // writes it: the editor renders empty and the effect below fills it. Give\n // the first render its own markup and this must emit `sanitize(newContent)`.\n setContent(newContent);\n onChange?.(newContent);\n }, [maxLength, onChange, content, editorRef]);\n\n return { content, handleContentChange };\n};\n\nconst useFocusState = ({ onFocus, onBlur }: { onFocus?: () => void; onBlur?: () => void }) => {\n const [isFocused, setIsFocused] = useState(false);\n\n const handleFocus = useCallback(() => {\n setIsFocused(true);\n onFocus?.();\n }, [onFocus]);\n\n const handleBlur = useCallback(() => {\n setIsFocused(false);\n onBlur?.();\n }, [onBlur]);\n\n return { isFocused, handleFocus, handleBlur };\n};\n\nexport const useRichTextEditor = ({\n value,\n onChange,\n onFocus,\n onBlur,\n disabled,\n readOnly,\n maxLength,\n}: {\n value?: string;\n onChange?: (value: string) => void;\n onFocus?: () => void;\n onBlur?: () => void;\n disabled?: boolean;\n readOnly?: boolean;\n maxLength?: number;\n}) => {\n const editorRef = useRef<HTMLDivElement>(null);\n const { content, handleContentChange } = useEditorContent({\n editorRef,\n value,\n onChange,\n maxLength,\n });\n const { isFocused, handleFocus, handleBlur } = useFocusState({ onFocus, onBlur });\n\n const applyFormat = useCallback(\n (formatType: string, formatValue?: string) => {\n if (disabled || readOnly || !editorRef.current) return;\n\n const selection = window.getSelection();\n // Nothing selected: give the editor focus so the next keystroke lands.\n if (!selection || selection.rangeCount === 0) {\n editorRef.current.focus();\n return;\n }\n\n applyFormatToRange({\n formatType,\n value: formatValue,\n range: selection.getRangeAt(0),\n editor: editorRef.current,\n });\n\n handleContentChange();\n editorRef.current.focus();\n },\n [disabled, readOnly, handleContentChange],\n );\n\n const sanitizedContent = sanitize(content);\n\n // Reflect the sanitized result back when it differs from what is on screen.\n useEffect(() => {\n if (editorRef.current && editorRef.current.innerHTML !== sanitizedContent) {\n editorRef.current.innerHTML = sanitizedContent;\n }\n }, [sanitizedContent]);\n\n return {\n editorRef,\n isFocused,\n characterCount: textLengthOf(sanitizedContent),\n handleContentChange,\n handleFocus,\n handleBlur,\n applyFormat,\n };\n};\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useRichTextEditor
3
- } from "./chunk-774B27QA.js";
3
+ } from "./chunk-EUACQR6N.js";
4
4
  import {
5
5
  RichTextEditorToolbar
6
6
  } from "./chunk-WVZBYTIK.js";
@@ -174,4 +174,4 @@ export {
174
174
  RichTextEditor,
175
175
  RichTextEditor_default
176
176
  };
177
- //# sourceMappingURL=chunk-7EM4THID.js.map
177
+ //# sourceMappingURL=chunk-RTEY5COP.js.map
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-IWEUSZY3.js";
7
7
  import {
8
8
  Separator
9
- } from "./chunk-XSSCSRUX.js";
9
+ } from "./chunk-X53F33KA.js";
10
10
  import {
11
11
  Card,
12
12
  CardContent
@@ -217,4 +217,4 @@ export {
217
217
  SocialLoginButton,
218
218
  SocialLoginContainer
219
219
  };
220
- //# sourceMappingURL=chunk-5KWHINO5.js.map
220
+ //# sourceMappingURL=chunk-SLDSWQVE.js.map
@@ -2,7 +2,7 @@ import {
2
2
  labelledSeparatorLayout,
3
3
  separatorBlockMargin,
4
4
  separatorStyles
5
- } from "./chunk-SYNB3IIB.js";
5
+ } from "./chunk-35443PF6.js";
6
6
 
7
7
  // src/components/layout/Separator/Separator.tsx
8
8
  import Box from "@mui/material/Box/index.js";
@@ -32,6 +32,8 @@ var Separator = ({
32
32
  {
33
33
  className,
34
34
  "data-testid": dataTestId,
35
+ role: "separator",
36
+ "aria-orientation": orientation,
35
37
  sx: {
36
38
  display: "flex",
37
39
  alignItems: "center",
@@ -71,4 +73,4 @@ var Separator = ({
71
73
  export {
72
74
  Separator
73
75
  };
74
- //# sourceMappingURL=chunk-XSSCSRUX.js.map
76
+ //# sourceMappingURL=chunk-X53F33KA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/layout/Separator/Separator.tsx"],"sourcesContent":["import Box from '@mui/material/Box/index.js';\nimport Typography from '@mui/material/Typography/index.js';\nimport { useTheme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport { labelledSeparatorLayout, separatorBlockMargin, separatorStyles } from './Separator.styles';\nimport type { SeparatorProps } from './Separator.types';\n\nexport const Separator: React.FC<SeparatorProps> = ({\n variant = 'solid',\n orientation = 'horizontal',\n size = 'md',\n color,\n margin,\n length,\n children,\n className,\n 'data-testid': dataTestId,\n}) => {\n const theme = useTheme();\n const isHorizontal = orientation === 'horizontal';\n const blockMargin = separatorBlockMargin(theme, size, margin, isHorizontal);\n const styles = separatorStyles(theme, { variant, orientation, size, color, blockMargin, length });\n\n if (children) {\n // The rules either side of the label sit INSIDE the row, which carries the\n // margin; their own would add it a second time.\n const layout = labelledSeparatorLayout(theme, isHorizontal, length);\n const ruleStyles = { ...styles, margin: 0, ...layout.rule };\n\n return (\n <Box\n className={className}\n // The plain branch below has always carried the caller's hook; this one\n // silently dropped it, so a LABELLED separator was the one variant no\n // test could address except through its own words.\n data-testid={dataTestId}\n // The same semantics as the plain branch, on the group, as MUI's\n // `Divider` does with children (FUT-2674). Without them a screen reader\n // heard the label with nothing marking a boundary. The label stays\n // inside, and the separator takes no name from it.\n role=\"separator\"\n aria-orientation={orientation}\n sx={{\n display: 'flex',\n alignItems: 'center',\n margin: blockMargin,\n flexDirection: isHorizontal ? 'row' : 'column',\n gap: theme.spacing(2),\n ...layout.group,\n }}\n >\n <Box sx={ruleStyles} />\n {/*\n No background behind the label. The two rules and the words are a FLEX\n ROW — the rules stop where the label starts — so there is nothing for a\n background to mask, and painting one made the label a coloured pill on\n every surface that is not `background.default`. On a white card it read\n as a highlight nobody had asked for.\n */}\n <Typography\n variant=\"body2\"\n color=\"text.secondary\"\n sx={{ flexShrink: 0, padding: theme.spacing(0, 1) }}\n >\n {children}\n </Typography>\n <Box sx={ruleStyles} />\n </Box>\n );\n }\n\n return (\n <Box\n className={className}\n sx={styles}\n role=\"separator\"\n aria-orientation={orientation}\n data-testid={dataTestId}\n />\n );\n};\n"],"mappings":";;;;;;;AAAA,OAAO,SAAS;AAChB,OAAO,gBAAgB;AACvB,SAAS,gBAAgB;AA6BnB,SAqBE,KArBF;AAvBC,IAAM,YAAsC,CAAC;AAAA,EAClD,UAAU;AAAA,EACV,cAAc;AAAA,EACd,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,MAAM;AACJ,QAAM,QAAQ,SAAS;AACvB,QAAM,eAAe,gBAAgB;AACrC,QAAM,cAAc,qBAAqB,OAAO,MAAM,QAAQ,YAAY;AAC1E,QAAM,SAAS,gBAAgB,OAAO,EAAE,SAAS,aAAa,MAAM,OAAO,aAAa,OAAO,CAAC;AAEhG,MAAI,UAAU;AAGZ,UAAM,SAAS,wBAAwB,OAAO,cAAc,MAAM;AAClE,UAAM,aAAa,EAAE,GAAG,QAAQ,QAAQ,GAAG,GAAG,OAAO,KAAK;AAE1D,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QAIA,eAAa;AAAA,QAKb,MAAK;AAAA,QACL,oBAAkB;AAAA,QAClB,IAAI;AAAA,UACF,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,QAAQ;AAAA,UACR,eAAe,eAAe,QAAQ;AAAA,UACtC,KAAK,MAAM,QAAQ,CAAC;AAAA,UACpB,GAAG,OAAO;AAAA,QACZ;AAAA,QAEA;AAAA,8BAAC,OAAI,IAAI,YAAY;AAAA,UAQrB;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,OAAM;AAAA,cACN,IAAI,EAAE,YAAY,GAAG,SAAS,MAAM,QAAQ,GAAG,CAAC,EAAE;AAAA,cAEjD;AAAA;AAAA,UACH;AAAA,UACA,oBAAC,OAAI,IAAI,YAAY;AAAA;AAAA;AAAA,IACvB;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,IAAI;AAAA,MACJ,MAAK;AAAA,MACL,oBAAkB;AAAA,MAClB,eAAa;AAAA;AAAA,EACf;AAEJ;","names":[]}
@@ -1,8 +1,8 @@
1
1
  import "../chunk-WMY276HD.js";
2
2
  import {
3
3
  RichTextEditor
4
- } from "../chunk-7EM4THID.js";
5
- import "../chunk-774B27QA.js";
4
+ } from "../chunk-RTEY5COP.js";
5
+ import "../chunk-EUACQR6N.js";
6
6
  import "../chunk-UZOG6HNN.js";
7
7
  import "../chunk-33LAPBYB.js";
8
8
  import "../chunk-WVZBYTIK.js";
@@ -1,8 +1,8 @@
1
1
  import "../chunk-CMMPGAMJ.js";
2
2
  import {
3
3
  Separator
4
- } from "../chunk-XSSCSRUX.js";
5
- import "../chunk-SYNB3IIB.js";
4
+ } from "../chunk-X53F33KA.js";
5
+ import "../chunk-35443PF6.js";
6
6
  import "../chunk-PZBK3F52.js";
7
7
  import "../chunk-YKCAJ2TN.js";
8
8
  import "../chunk-PZ5AY32C.js";
@@ -4,15 +4,15 @@ import {
4
4
  GoogleIcon,
5
5
  SocialLoginButton,
6
6
  SocialLoginContainer
7
- } from "./chunk-5KWHINO5.js";
7
+ } from "./chunk-SLDSWQVE.js";
8
8
  import "./chunk-INGPH3YQ.js";
9
9
  import "./chunk-6GP2DOOO.js";
10
10
  import "./chunk-IWEUSZY3.js";
11
11
  import "./chunk-RB3X6CEE.js";
12
12
  import "./chunk-CYZZHWNK.js";
13
13
  import "./chunk-CMMPGAMJ.js";
14
- import "./chunk-XSSCSRUX.js";
15
- import "./chunk-SYNB3IIB.js";
14
+ import "./chunk-X53F33KA.js";
15
+ import "./chunk-35443PF6.js";
16
16
  import "./chunk-VUHLNYAE.js";
17
17
  import "./chunk-2GSIEDGN.js";
18
18
  import "./chunk-WN5PNP6F.js";
@@ -1 +1 @@
1
- {"version":3,"file":"RichTextEditor.hooks.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/RichTextEditor/RichTextEditor.hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAwE/B,eAAO,MAAM,iBAAiB,GAAI,sEAQ/B;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;;;;;;;8BAWgB,MAAM,gBAAgB,MAAM;CAyC5C,CAAC"}
1
+ {"version":3,"file":"RichTextEditor.hooks.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/RichTextEditor/RichTextEditor.hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAyF/B,eAAO,MAAM,iBAAiB,GAAI,sEAQ/B;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;;;;;;;8BAWgB,MAAM,gBAAgB,MAAM;CAyC5C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Separator.d.ts","sourceRoot":"","sources":["../../../../../src/components/layout/Separator/Separator.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAmE9C,CAAC"}
1
+ {"version":3,"file":"Separator.d.ts","sourceRoot":"","sources":["../../../../../src/components/layout/Separator/Separator.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAyE9C,CAAC"}
@@ -18,15 +18,23 @@ type SeparatorStyleArgs = Required<Pick<SeparatorProps, 'variant' | 'orientation
18
18
  export declare const separatorBlockMargin: (theme: Theme, size: SeparatorSize, margin: number | string | undefined, isHorizontal: boolean) => string;
19
19
  /**
20
20
  * What a labelled separator's rules add to {@link separatorStyles}, and whether
21
- * the group stretches to its parent (FUT-2617).
21
+ * the group stretches to its parent (FUT-2617, FUT-2675).
22
22
  *
23
23
  * A labelled VERTICAL separator is a flex COLUMN — rule, label, rule — and with
24
24
  * no `length` each rule is `height: 100%` of a column whose height is its own
25
25
  * content: both resolved to 0px and only the label showed. So the column
26
26
  * stretches to the row it sits in and the two rules split what the label
27
- * leaves, equally (`flex: 1 1 0`), never shorter than a visible minimum. With a
28
- * `length` the rule is exactly that long: no grow, no shrink, no basis, and the
29
- * column keeps its content height, centred as before.
27
+ * leaves, equally (`flex: 1 1 0`), never shorter than a visible minimum.
28
+ *
29
+ * A RELATIVE `length` (FUT-2675) resolved against that same content-height
30
+ * column and drew 0px too. It now means what it means on a horizontal labelled
31
+ * separator: that fraction of the run. The column stretches as above and each
32
+ * rule is `length` of it, with no grow and the default shrink, so rule, label
33
+ * and rule never overflow the row; the same minimum holds, and is all a block
34
+ * parent (where `align-self` does nothing) gets.
35
+ *
36
+ * An ABSOLUTE `length` is exactly that long: no grow, no shrink, no basis, and
37
+ * the column keeps its content height, centred as before.
30
38
  *
31
39
  * Only the labelled branch reads this. The plain separator keeps
32
40
  * `separatorStyles` as is, since `flex: 1 1 0` there would widen a vertical
@@ -1 +1 @@
1
- {"version":3,"file":"Separator.styles.d.ts","sourceRoot":"","sources":["../../../../../src/components/layout/Separator/Separator.styles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAEtE,OAAO,KAAK,EAEV,cAAc,EACd,aAAa,EAEd,MAAM,mBAAmB,CAAC;AAuB3B,+DAA+D;AAC/D,eAAO,MAAM,eAAe,GAC1B,MAAM,aAAa,EACnB,SAAS,MAAM,GAAG,MAAM,KACvB,MAAM,GAAG,MAAmD,CAAC;AAyDhE,KAAK,kBAAkB,GAAG,QAAQ,CAChC,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC,CACzD,GACC,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,QAAQ,CAAC,GAAG;IACzC,+FAA+F;IAC/F,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,KAAK,EACZ,MAAM,aAAa,EACnB,QAAQ,MAAM,GAAG,MAAM,GAAG,SAAS,EACnC,cAAc,OAAO,KACpB,MAIF,CAAC;AAaF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uBAAuB,GAClC,OAAO,KAAK,EACZ,cAAc,OAAO,EACrB,QAAQ,cAAc,CAAC,QAAQ,CAAC,KAC/B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CASrC,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,OAAO,KAAK,EACZ,4DAA4D,kBAAkB,KAC7E,SA8BF,CAAC"}
1
+ {"version":3,"file":"Separator.styles.d.ts","sourceRoot":"","sources":["../../../../../src/components/layout/Separator/Separator.styles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAEtE,OAAO,KAAK,EAEV,cAAc,EACd,aAAa,EAEd,MAAM,mBAAmB,CAAC;AAuB3B,+DAA+D;AAC/D,eAAO,MAAM,eAAe,GAC1B,MAAM,aAAa,EACnB,SAAS,MAAM,GAAG,MAAM,KACvB,MAAM,GAAG,MAAmD,CAAC;AAyDhE,KAAK,kBAAkB,GAAG,QAAQ,CAChC,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC,CACzD,GACC,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,QAAQ,CAAC,GAAG;IACzC,+FAA+F;IAC/F,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,KAAK,EACZ,MAAM,aAAa,EACnB,QAAQ,MAAM,GAAG,MAAM,GAAG,SAAS,EACnC,cAAc,OAAO,KACpB,MAIF,CAAC;AAqBF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,uBAAuB,GAClC,OAAO,KAAK,EACZ,cAAc,OAAO,EACrB,QAAQ,cAAc,CAAC,QAAQ,CAAC,KAC/B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAWrC,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,OAAO,KAAK,EACZ,4DAA4D,kBAAkB,KAC7E,SA8BF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/ui",
3
- "version": "6.39.11",
3
+ "version": "6.39.13",
4
4
  "license": "MIT",
5
5
  "packageManager": "pnpm@10.34.5",
6
6
  "type": "module",
@@ -16,6 +16,13 @@ const SANITIZE_CONFIG = {
16
16
  KEEP_CONTENT: true,
17
17
  };
18
18
 
19
+ // The one way editor html reaches the DOM. Both writes (the render-path effect
20
+ // and the over-limit restore) use it, so they cannot drift to different configs.
21
+ const sanitize = (html: string): string => DOMPurify.sanitize(html, SANITIZE_CONFIG);
22
+
23
+ // INVARIANT: only ever pass SANITISED html here. Assigning innerHTML to a
24
+ // detached element still fetches `<img src>` and fires its `onerror`, so raw
25
+ // markup would execute. Its one caller passes `sanitizedContent`.
19
26
  const textLengthOf = (html: string): number => {
20
27
  const probe = document.createElement('div');
21
28
  probe.innerHTML = html;
@@ -34,6 +41,11 @@ const useEditorContent = ({
34
41
  onChange?: (value: string) => void;
35
42
  maxLength?: number;
36
43
  }) => {
44
+ // `content` holds UNSANITISED html: the caller's raw `value` (read once, at
45
+ // mount) until the first accepted input, and the editor's raw live innerHTML
46
+ // after it. Every write of it to the DOM must go through `sanitize` below.
47
+ // INVARIANT: if syncing a later `value` is ever added, the new value reaches
48
+ // the DOM through the same sanitiser, never by a direct innerHTML write.
37
49
  const [content, setContent] = useState(value ?? '');
38
50
 
39
51
  const handleContentChange = useCallback(() => {
@@ -42,12 +54,17 @@ const useEditorContent = ({
42
54
  const newContent = editorRef.current.innerHTML;
43
55
 
44
56
  // Over the limit, put the last accepted content back rather than truncating
45
- // mid-markup.
57
+ // mid-markup. `content` may be the caller's raw `value`, so the restore is
58
+ // sanitised exactly as the render path is: writing it raw ran an `onerror`
59
+ // handler carried in `value` (FUT-2685).
46
60
  if (maxLength && (editorRef.current.textContent || '').length > maxLength) {
47
- editorRef.current.innerHTML = content;
61
+ editorRef.current.innerHTML = sanitize(content);
48
62
  return;
49
63
  }
50
64
 
65
+ // Emitting the live DOM is safe only because nothing but `sanitize()` ever
66
+ // writes it: the editor renders empty and the effect below fills it. Give
67
+ // the first render its own markup and this must emit `sanitize(newContent)`.
51
68
  setContent(newContent);
52
69
  onChange?.(newContent);
53
70
  }, [maxLength, onChange, content, editorRef]);
@@ -121,7 +138,7 @@ export const useRichTextEditor = ({
121
138
  [disabled, readOnly, handleContentChange],
122
139
  );
123
140
 
124
- const sanitizedContent = DOMPurify.sanitize(content, SANITIZE_CONFIG);
141
+ const sanitizedContent = sanitize(content);
125
142
 
126
143
  // Reflect the sanitized result back when it differs from what is on screen.
127
144
  useEffect(() => {
@@ -128,17 +128,33 @@ const ruleLength = (theme: Theme, length: SeparatorProps['length']): string => {
128
128
  return length <= 1 ? `${length * 100}%` : rem(theme, length);
129
129
  };
130
130
 
131
+ /**
132
+ * Whether a set `length` is a fraction of the parent rather than a fixed
133
+ * extent: a number of 1 or less, as {@link ruleLength} reads it, or any string
134
+ * that carries a percentage (`'50%'`, `'calc(50% - 8px)'`).
135
+ */
136
+ const isRelativeLength = (length: number | string): boolean =>
137
+ typeof length === 'number' ? length <= 1 : length.includes('%');
138
+
131
139
  /**
132
140
  * What a labelled separator's rules add to {@link separatorStyles}, and whether
133
- * the group stretches to its parent (FUT-2617).
141
+ * the group stretches to its parent (FUT-2617, FUT-2675).
134
142
  *
135
143
  * A labelled VERTICAL separator is a flex COLUMN — rule, label, rule — and with
136
144
  * no `length` each rule is `height: 100%` of a column whose height is its own
137
145
  * content: both resolved to 0px and only the label showed. So the column
138
146
  * stretches to the row it sits in and the two rules split what the label
139
- * leaves, equally (`flex: 1 1 0`), never shorter than a visible minimum. With a
140
- * `length` the rule is exactly that long: no grow, no shrink, no basis, and the
141
- * column keeps its content height, centred as before.
147
+ * leaves, equally (`flex: 1 1 0`), never shorter than a visible minimum.
148
+ *
149
+ * A RELATIVE `length` (FUT-2675) resolved against that same content-height
150
+ * column and drew 0px too. It now means what it means on a horizontal labelled
151
+ * separator: that fraction of the run. The column stretches as above and each
152
+ * rule is `length` of it, with no grow and the default shrink, so rule, label
153
+ * and rule never overflow the row; the same minimum holds, and is all a block
154
+ * parent (where `align-self` does nothing) gets.
155
+ *
156
+ * An ABSOLUTE `length` is exactly that long: no grow, no shrink, no basis, and
157
+ * the column keeps its content height, centred as before.
142
158
  *
143
159
  * Only the labelled branch reads this. The plain separator keeps
144
160
  * `separatorStyles` as is, since `flex: 1 1 0` there would widen a vertical
@@ -151,11 +167,13 @@ export const labelledSeparatorLayout = (
151
167
  length: SeparatorProps['length'],
152
168
  ): { rule: CSSObject; group: CSSObject } => {
153
169
  if (isHorizontal) return { rule: {}, group: {} };
154
- if (length) return { rule: { flex: 'none' }, group: {} };
170
+ if (length && !isRelativeLength(length)) return { rule: { flex: 'none' }, group: {} };
155
171
  return {
156
- // A design 16px, as long as the gap beside the label at the default
157
- // spacing, so a short or unsized row still shows two rules and not a word.
158
- rule: { flex: '1 1 0', minHeight: rem(theme, 16) },
172
+ // Unset, the rules share the run; relative, each is its own fraction of it.
173
+ // Either way a design 16px floor, as long as the gap beside the label at
174
+ // the default spacing, so a short or unsized row still shows two rules and
175
+ // not a word.
176
+ rule: { flex: length ? '0 1 auto' : '1 1 0', minHeight: rem(theme, 16) },
159
177
  group: { alignSelf: 'stretch' },
160
178
  };
161
179
  };
@@ -35,6 +35,12 @@ export const Separator: React.FC<SeparatorProps> = ({
35
35
  // silently dropped it, so a LABELLED separator was the one variant no
36
36
  // test could address except through its own words.
37
37
  data-testid={dataTestId}
38
+ // The same semantics as the plain branch, on the group, as MUI's
39
+ // `Divider` does with children (FUT-2674). Without them a screen reader
40
+ // heard the label with nothing marking a boundary. The label stays
41
+ // inside, and the separator takes no name from it.
42
+ role="separator"
43
+ aria-orientation={orientation}
38
44
  sx={{
39
45
  display: 'flex',
40
46
  alignItems: 'center',
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/form/RichTextEditor/RichTextEditor.hooks.ts"],"sourcesContent":["import DOMPurify from 'dompurify';\nimport type React from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { applyFormatToRange } from './RichTextEditor.format';\n\n// Deliberately narrow: anything not listed is stripped, and no data-* attributes\n// survive, so pasted markup cannot smuggle in behaviour.\nconst SANITIZE_CONFIG = {\n ALLOWED_TAGS: [\n 'p', 'br', 'strong', 'em', 'u', 's', 'a', 'img', 'ul', 'ol', 'li',\n 'blockquote', 'pre', 'code', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'div',\n ],\n ALLOWED_ATTR: ['href', 'src', 'alt', 'target', 'rel', 'style', 'class'],\n ALLOW_DATA_ATTR: false,\n KEEP_CONTENT: true,\n};\n\nconst textLengthOf = (html: string): number => {\n const probe = document.createElement('div');\n probe.innerHTML = html;\n return probe.textContent?.length || 0;\n};\n\n// The editable content and the limit that guards it.\nconst useEditorContent = ({\n editorRef,\n value,\n onChange,\n maxLength,\n}: {\n editorRef: React.RefObject<HTMLDivElement | null>;\n value?: string;\n onChange?: (value: string) => void;\n maxLength?: number;\n}) => {\n const [content, setContent] = useState(value ?? '');\n\n const handleContentChange = useCallback(() => {\n if (!editorRef.current) return;\n\n const newContent = editorRef.current.innerHTML;\n\n // Over the limit, put the last accepted content back rather than truncating\n // mid-markup.\n if (maxLength && (editorRef.current.textContent || '').length > maxLength) {\n editorRef.current.innerHTML = content;\n return;\n }\n\n setContent(newContent);\n onChange?.(newContent);\n }, [maxLength, onChange, content, editorRef]);\n\n return { content, handleContentChange };\n};\n\nconst useFocusState = ({ onFocus, onBlur }: { onFocus?: () => void; onBlur?: () => void }) => {\n const [isFocused, setIsFocused] = useState(false);\n\n const handleFocus = useCallback(() => {\n setIsFocused(true);\n onFocus?.();\n }, [onFocus]);\n\n const handleBlur = useCallback(() => {\n setIsFocused(false);\n onBlur?.();\n }, [onBlur]);\n\n return { isFocused, handleFocus, handleBlur };\n};\n\nexport const useRichTextEditor = ({\n value,\n onChange,\n onFocus,\n onBlur,\n disabled,\n readOnly,\n maxLength,\n}: {\n value?: string;\n onChange?: (value: string) => void;\n onFocus?: () => void;\n onBlur?: () => void;\n disabled?: boolean;\n readOnly?: boolean;\n maxLength?: number;\n}) => {\n const editorRef = useRef<HTMLDivElement>(null);\n const { content, handleContentChange } = useEditorContent({\n editorRef,\n value,\n onChange,\n maxLength,\n });\n const { isFocused, handleFocus, handleBlur } = useFocusState({ onFocus, onBlur });\n\n const applyFormat = useCallback(\n (formatType: string, formatValue?: string) => {\n if (disabled || readOnly || !editorRef.current) return;\n\n const selection = window.getSelection();\n // Nothing selected: give the editor focus so the next keystroke lands.\n if (!selection || selection.rangeCount === 0) {\n editorRef.current.focus();\n return;\n }\n\n applyFormatToRange({\n formatType,\n value: formatValue,\n range: selection.getRangeAt(0),\n editor: editorRef.current,\n });\n\n handleContentChange();\n editorRef.current.focus();\n },\n [disabled, readOnly, handleContentChange],\n );\n\n const sanitizedContent = DOMPurify.sanitize(content, SANITIZE_CONFIG);\n\n // Reflect the sanitized result back when it differs from what is on screen.\n useEffect(() => {\n if (editorRef.current && editorRef.current.innerHTML !== sanitizedContent) {\n editorRef.current.innerHTML = sanitizedContent;\n }\n }, [sanitizedContent]);\n\n return {\n editorRef,\n isFocused,\n characterCount: textLengthOf(sanitizedContent),\n handleContentChange,\n handleFocus,\n handleBlur,\n applyFormat,\n };\n};\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/layout/Separator/Separator.styles.ts"],"sourcesContent":["import type { CSSObject, Theme } from '@mui/material/styles/index.js';\n\nimport type {\n SeparatorOrientation,\n SeparatorProps,\n SeparatorSize,\n SeparatorVariant,\n} from './Separator.types';\nimport { FIELD_BORDER_WIDTH } from '../../../tokens/field-height.core';\nimport { rem } from '../../../tokens/relative';\n\nconst THICKNESS_PX: Record<SeparatorSize, number> = {\n xs: 1,\n sm: 2,\n md: 3,\n lg: 4,\n xl: 6,\n};\n\nconst MARGIN_PX: Record<SeparatorSize, number> = {\n xs: 4,\n sm: 8,\n md: 16,\n lg: 24,\n xl: 32,\n};\n\nconst separatorThickness = (size: SeparatorSize): number =>\n THICKNESS_PX[size] ?? THICKNESS_PX.md;\n\n/** An explicit `margin` wins; otherwise the size picks one. */\nexport const separatorMargin = (\n size: SeparatorSize,\n margin?: number | string,\n): number | string => margin ?? MARGIN_PX[size] ?? MARGIN_PX.md;\n\n// `gradient` paints a background rather than a border, so it draws its line as a\n// solid border of zero visual consequence.\nconst borderStyleFor = (variant: SeparatorVariant): 'dashed' | 'dotted' | 'solid' => {\n switch (variant) {\n case 'dashed':\n return 'dashed';\n case 'dotted':\n return 'dotted';\n default:\n return 'solid';\n }\n};\n\nconst gradientBackground = (\n variant: SeparatorVariant,\n orientation: SeparatorOrientation,\n color: string,\n): string | undefined => {\n if (variant !== 'gradient') return undefined;\n const angle = orientation === 'horizontal' ? '90deg' : '180deg';\n return `linear-gradient(${angle}, transparent 0%, ${color} 50%, transparent 100%)`;\n};\n\n// Only the leading edge carries the rule; the other three are zeroed explicitly so\n// a parent's border shorthand cannot leak through.\nconst borderEdges = (\n isHorizontal: boolean,\n thickness: string,\n style: 'dashed' | 'dotted' | 'solid',\n color: string,\n): CSSObject => {\n const leading = isHorizontal ? 'Top' : 'Left';\n const blank: CSSObject = {\n borderTopWidth: 0,\n borderBottomWidth: 0,\n borderLeftWidth: 0,\n borderRightWidth: 0,\n borderTopStyle: 'none',\n borderBottomStyle: 'none',\n borderLeftStyle: 'none',\n borderRightStyle: 'none',\n borderTopColor: 'transparent',\n borderBottomColor: 'transparent',\n borderLeftColor: 'transparent',\n borderRightColor: 'transparent',\n };\n\n return {\n ...blank,\n [`border${leading}Width`]: thickness,\n [`border${leading}Style`]: style,\n [`border${leading}Color`]: color,\n };\n};\n\ntype SeparatorStyleArgs = Required<\n Pick<SeparatorProps, 'variant' | 'orientation' | 'size'>\n> &\n Pick<SeparatorProps, 'color' | 'length'> & {\n /** From {@link separatorBlockMargin}, which both the plain and the labelled separator read. */\n blockMargin: string;\n };\n\n/**\n * The separator's margin, on the axis it separates along only: above and below\n * a horizontal rule, either side of a vertical one. A margin given as a string\n * carries its own units; a number is design px, through the type scale.\n *\n * The LABELLED separator uses this too. It used to hand the number straight to\n * `sx.margin`, where a number is SPACING UNITS — `md`'s 16 drew 128px on all\n * four sides, against 16px above and below for the unlabelled one.\n */\nexport const separatorBlockMargin = (\n theme: Theme,\n size: SeparatorSize,\n margin: number | string | undefined,\n isHorizontal: boolean,\n): string => {\n const value = separatorMargin(size, margin);\n const length = typeof value === 'string' ? value : rem(theme, value);\n return isHorizontal ? `${length} 0` : `0 ${length}`;\n};\n\n/**\n * The rule's extent along its axis. It lands in `sx`, which reads a number of 1\n * or less as a fraction of the parent, and that stays so; any other number is\n * design px, through the type scale. Unset (or `0`/`''`) is the full run.\n */\nconst ruleLength = (theme: Theme, length: SeparatorProps['length']): string => {\n if (!length) return '100%';\n if (typeof length === 'string') return length;\n return length <= 1 ? `${length * 100}%` : rem(theme, length);\n};\n\n/**\n * What a labelled separator's rules add to {@link separatorStyles}, and whether\n * the group stretches to its parent (FUT-2617).\n *\n * A labelled VERTICAL separator is a flex COLUMN — rule, label, rule — and with\n * no `length` each rule is `height: 100%` of a column whose height is its own\n * content: both resolved to 0px and only the label showed. So the column\n * stretches to the row it sits in and the two rules split what the label\n * leaves, equally (`flex: 1 1 0`), never shorter than a visible minimum. With a\n * `length` the rule is exactly that long: no grow, no shrink, no basis, and the\n * column keeps its content height, centred as before.\n *\n * Only the labelled branch reads this. The plain separator keeps\n * `separatorStyles` as is, since `flex: 1 1 0` there would widen a vertical\n * separator sitting in a row. The horizontal rules already shrink around the\n * label in their row, so they are left alone.\n */\nexport const labelledSeparatorLayout = (\n theme: Theme,\n isHorizontal: boolean,\n length: SeparatorProps['length'],\n): { rule: CSSObject; group: CSSObject } => {\n if (isHorizontal) return { rule: {}, group: {} };\n if (length) return { rule: { flex: 'none' }, group: {} };\n return {\n // A design 16px, as long as the gap beside the label at the default\n // spacing, so a short or unsized row still shows two rules and not a word.\n rule: { flex: '1 1 0', minHeight: rem(theme, 16) },\n group: { alignSelf: 'stretch' },\n };\n};\n\nexport const separatorStyles = (\n theme: Theme,\n { variant, orientation, size, color, blockMargin, length }: SeparatorStyleArgs,\n): CSSObject => {\n const isHorizontal = orientation === 'horizontal';\n // `xs` is the 1px hairline: it stays one device-independent pixel at every\n // root size, like every other hairline in the package; heavier rules scale.\n const thicknessPx = separatorThickness(size);\n const thickness = thicknessPx === FIELD_BORDER_WIDTH ? `${FIELD_BORDER_WIDTH}px` : rem(theme, thicknessPx);\n const along = ruleLength(theme, length);\n const resolvedColor = color || theme.palette.divider;\n\n const baseStyles: CSSObject = {\n display: 'flex',\n alignItems: 'center',\n margin: blockMargin,\n width: isHorizontal ? along : thickness,\n height: isHorizontal ? thickness : along,\n boxSizing: 'border-box',\n };\n\n if (variant === 'gradient') {\n return {\n ...baseStyles,\n background: gradientBackground(variant, orientation, resolvedColor),\n };\n }\n\n return {\n ...baseStyles,\n backgroundColor: 'transparent',\n ...borderEdges(isHorizontal, thickness, borderStyleFor(variant), resolvedColor),\n };\n};\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/layout/Separator/Separator.tsx"],"sourcesContent":["import Box from '@mui/material/Box/index.js';\nimport Typography from '@mui/material/Typography/index.js';\nimport { useTheme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport { labelledSeparatorLayout, separatorBlockMargin, separatorStyles } from './Separator.styles';\nimport type { SeparatorProps } from './Separator.types';\n\nexport const Separator: React.FC<SeparatorProps> = ({\n variant = 'solid',\n orientation = 'horizontal',\n size = 'md',\n color,\n margin,\n length,\n children,\n className,\n 'data-testid': dataTestId,\n}) => {\n const theme = useTheme();\n const isHorizontal = orientation === 'horizontal';\n const blockMargin = separatorBlockMargin(theme, size, margin, isHorizontal);\n const styles = separatorStyles(theme, { variant, orientation, size, color, blockMargin, length });\n\n if (children) {\n // The rules either side of the label sit INSIDE the row, which carries the\n // margin; their own would add it a second time.\n const layout = labelledSeparatorLayout(theme, isHorizontal, length);\n const ruleStyles = { ...styles, margin: 0, ...layout.rule };\n\n return (\n <Box\n className={className}\n // The plain branch below has always carried the caller's hook; this one\n // silently dropped it, so a LABELLED separator was the one variant no\n // test could address except through its own words.\n data-testid={dataTestId}\n sx={{\n display: 'flex',\n alignItems: 'center',\n margin: blockMargin,\n flexDirection: isHorizontal ? 'row' : 'column',\n gap: theme.spacing(2),\n ...layout.group,\n }}\n >\n <Box sx={ruleStyles} />\n {/*\n No background behind the label. The two rules and the words are a FLEX\n ROW — the rules stop where the label starts — so there is nothing for a\n background to mask, and painting one made the label a coloured pill on\n every surface that is not `background.default`. On a white card it read\n as a highlight nobody had asked for.\n */}\n <Typography\n variant=\"body2\"\n color=\"text.secondary\"\n sx={{ flexShrink: 0, padding: theme.spacing(0, 1) }}\n >\n {children}\n </Typography>\n <Box sx={ruleStyles} />\n </Box>\n );\n }\n\n return (\n <Box\n className={className}\n sx={styles}\n role=\"separator\"\n aria-orientation={orientation}\n data-testid={dataTestId}\n />\n );\n};\n"],"mappings":";;;;;;;AAAA,OAAO,SAAS;AAChB,OAAO,gBAAgB;AACvB,SAAS,gBAAgB;AA6BnB,SAeE,KAfF;AAvBC,IAAM,YAAsC,CAAC;AAAA,EAClD,UAAU;AAAA,EACV,cAAc;AAAA,EACd,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,MAAM;AACJ,QAAM,QAAQ,SAAS;AACvB,QAAM,eAAe,gBAAgB;AACrC,QAAM,cAAc,qBAAqB,OAAO,MAAM,QAAQ,YAAY;AAC1E,QAAM,SAAS,gBAAgB,OAAO,EAAE,SAAS,aAAa,MAAM,OAAO,aAAa,OAAO,CAAC;AAEhG,MAAI,UAAU;AAGZ,UAAM,SAAS,wBAAwB,OAAO,cAAc,MAAM;AAClE,UAAM,aAAa,EAAE,GAAG,QAAQ,QAAQ,GAAG,GAAG,OAAO,KAAK;AAE1D,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QAIA,eAAa;AAAA,QACb,IAAI;AAAA,UACF,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,QAAQ;AAAA,UACR,eAAe,eAAe,QAAQ;AAAA,UACtC,KAAK,MAAM,QAAQ,CAAC;AAAA,UACpB,GAAG,OAAO;AAAA,QACZ;AAAA,QAEA;AAAA,8BAAC,OAAI,IAAI,YAAY;AAAA,UAQrB;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,OAAM;AAAA,cACN,IAAI,EAAE,YAAY,GAAG,SAAS,MAAM,QAAQ,GAAG,CAAC,EAAE;AAAA,cAEjD;AAAA;AAAA,UACH;AAAA,UACA,oBAAC,OAAI,IAAI,YAAY;AAAA;AAAA;AAAA,IACvB;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,IAAI;AAAA,MACJ,MAAK;AAAA,MACL,oBAAkB;AAAA,MAClB,eAAa;AAAA;AAAA,EACf;AAEJ;","names":[]}