@scalewing/react 1.40.0 → 1.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +274 -7
  3. package/dist/calendar-labels.d.ts +2 -0
  4. package/dist/calendar-labels.js +7 -0
  5. package/dist/components/Article.d.ts +60 -0
  6. package/dist/components/Article.js +42 -0
  7. package/dist/components/BarChart.d.ts +20 -4
  8. package/dist/components/BarChart.js +13 -6
  9. package/dist/components/ButtonGroup.d.ts +8 -1
  10. package/dist/components/ButtonGroup.js +11 -2
  11. package/dist/components/DateField.d.ts +14 -2
  12. package/dist/components/DateField.js +14 -11
  13. package/dist/components/DenominationGrid.d.ts +3 -2
  14. package/dist/components/DenominationGrid.js +21 -3
  15. package/dist/components/Dialog.d.ts +29 -13
  16. package/dist/components/Dialog.js +22 -20
  17. package/dist/components/DocumentViewer.d.ts +41 -0
  18. package/dist/components/DocumentViewer.js +72 -0
  19. package/dist/components/Field.d.ts +13 -2
  20. package/dist/components/Field.js +14 -38
  21. package/dist/components/FileField.d.ts +44 -0
  22. package/dist/components/FileField.js +66 -0
  23. package/dist/components/FilterChips.d.ts +7 -37
  24. package/dist/components/FilterChips.js +32 -24
  25. package/dist/components/ListRow.d.ts +33 -3
  26. package/dist/components/ListRow.js +18 -17
  27. package/dist/components/MarkdownView.d.ts +39 -0
  28. package/dist/components/MarkdownView.js +30 -0
  29. package/dist/components/NavCount.d.ts +15 -2
  30. package/dist/components/NavCount.js +16 -6
  31. package/dist/components/SegmentedControl.d.ts +17 -1
  32. package/dist/components/SegmentedControl.js +11 -3
  33. package/dist/components/Switch.d.ts +16 -1
  34. package/dist/components/Switch.js +17 -6
  35. package/dist/components/action-bar/ActionBarSummary.d.ts +10 -2
  36. package/dist/components/action-bar/ActionBarSummary.js +11 -2
  37. package/dist/components/chart/use-chart-width.js +2 -24
  38. package/dist/components/date-field/CalendarEntryButton.d.ts +24 -0
  39. package/dist/components/date-field/CalendarEntryButton.js +16 -0
  40. package/dist/components/date-field/TypedEntry.d.ts +26 -0
  41. package/dist/components/date-field/TypedEntry.js +13 -0
  42. package/dist/components/denomination-grid/CountCell.d.ts +16 -0
  43. package/dist/components/denomination-grid/CountCell.js +14 -0
  44. package/dist/components/denomination-grid/DenominationStrip.d.ts +4 -1
  45. package/dist/components/denomination-grid/DenominationStrip.js +2 -2
  46. package/dist/components/denomination-grid/EntryCell.d.ts +3 -1
  47. package/dist/components/denomination-grid/EntryCell.js +2 -2
  48. package/dist/components/denomination-grid/StripRow.js +1 -10
  49. package/dist/components/denomination-grid/TransposedStrip.d.ts +17 -0
  50. package/dist/components/denomination-grid/TransposedStrip.js +43 -0
  51. package/dist/components/denomination-grid/types.d.ts +31 -0
  52. package/dist/components/denomination-grid/types.js +7 -1
  53. package/dist/components/denomination-grid/validate-options.d.ts +7 -0
  54. package/dist/components/denomination-grid/validate-options.js +27 -0
  55. package/dist/components/dialog/DialogFooter.d.ts +6 -5
  56. package/dist/components/dialog/DialogFooter.js +6 -6
  57. package/dist/components/dialog/validate.d.ts +24 -0
  58. package/dist/components/dialog/validate.js +23 -0
  59. package/dist/components/document-viewer/document-viewer-labels.d.ts +23 -0
  60. package/dist/components/document-viewer/document-viewer-labels.js +23 -0
  61. package/dist/components/document-viewer/page-box.d.ts +8 -0
  62. package/dist/components/document-viewer/page-box.js +12 -0
  63. package/dist/components/document-viewer/pdf-document.d.ts +32 -0
  64. package/dist/components/document-viewer/pdf-document.js +66 -0
  65. package/dist/components/document-viewer/pdf-options.d.ts +36 -0
  66. package/dist/components/document-viewer/pdf-options.js +46 -0
  67. package/dist/components/document-viewer/render-pdf-page.d.ts +20 -0
  68. package/dist/components/document-viewer/render-pdf-page.js +38 -0
  69. package/dist/components/document-viewer/scroll-chain.d.ts +6 -0
  70. package/dist/components/document-viewer/scroll-chain.js +23 -0
  71. package/dist/components/document-viewer/use-error-report.d.ts +5 -0
  72. package/dist/components/document-viewer/use-error-report.js +13 -0
  73. package/dist/components/document-viewer/use-page-render.d.ts +21 -0
  74. package/dist/components/document-viewer/use-page-render.js +70 -0
  75. package/dist/components/document-viewer/use-pdf-document.d.ts +22 -0
  76. package/dist/components/document-viewer/use-pdf-document.js +47 -0
  77. package/dist/components/document-viewer/use-pinch-zoom.d.ts +12 -0
  78. package/dist/components/document-viewer/use-pinch-zoom.js +67 -0
  79. package/dist/components/document-viewer/use-zoom.d.ts +23 -0
  80. package/dist/components/document-viewer/use-zoom.js +70 -0
  81. package/dist/components/document-viewer/zoom.d.ts +29 -0
  82. package/dist/components/document-viewer/zoom.js +37 -0
  83. package/dist/components/field/field-checks.d.ts +34 -0
  84. package/dist/components/field/field-checks.js +42 -0
  85. package/dist/components/file-field/file-field-checks.d.ts +13 -0
  86. package/dist/components/file-field/file-field-checks.js +21 -0
  87. package/dist/components/file-field/use-file-drop.d.ts +34 -0
  88. package/dist/components/file-field/use-file-drop.js +78 -0
  89. package/dist/components/file-field/use-swap-focus.d.ts +15 -0
  90. package/dist/components/file-field/use-swap-focus.js +40 -0
  91. package/dist/components/filter-chips/FilterChip.d.ts +16 -0
  92. package/dist/components/filter-chips/FilterChip.js +20 -0
  93. package/dist/components/filter-chips/options.d.ts +75 -0
  94. package/dist/components/filter-chips/options.js +11 -0
  95. package/dist/components/filter-chips/validate.d.ts +9 -0
  96. package/dist/components/filter-chips/validate.js +48 -0
  97. package/dist/components/list/ListRowContent.d.ts +8 -2
  98. package/dist/components/list/ListRowContent.js +6 -3
  99. package/dist/components/list/ListRowItem.d.ts +10 -0
  100. package/dist/components/list/ListRowItem.js +17 -0
  101. package/dist/components/list/row-naming.d.ts +17 -0
  102. package/dist/components/list/row-naming.js +19 -0
  103. package/dist/components/list/row-slot.d.ts +8 -0
  104. package/dist/components/list/row-slot.js +12 -0
  105. package/dist/components/markdown/markdown-limits.d.ts +14 -0
  106. package/dist/components/markdown/markdown-limits.js +14 -0
  107. package/dist/components/markdown/markdown-model.d.ts +94 -0
  108. package/dist/components/markdown/markdown-model.js +8 -0
  109. package/dist/components/markdown/markdown-options.d.ts +18 -0
  110. package/dist/components/markdown/markdown-options.js +10 -0
  111. package/dist/components/markdown/markdown-rules.d.ts +28 -0
  112. package/dist/components/markdown/markdown-rules.js +81 -0
  113. package/dist/components/markdown/markdown-text.d.ts +17 -0
  114. package/dist/components/markdown/markdown-text.js +86 -0
  115. package/dist/components/markdown/parse-markdown.d.ts +4 -0
  116. package/dist/components/markdown/parse-markdown.js +21 -0
  117. package/dist/components/markdown/read-blocks.d.ts +8 -0
  118. package/dist/components/markdown/read-blocks.js +168 -0
  119. package/dist/components/markdown/read-inlines.d.ts +12 -0
  120. package/dist/components/markdown/read-inlines.js +126 -0
  121. package/dist/components/markdown/read-markdown.d.ts +9 -0
  122. package/dist/components/markdown/read-markdown.js +27 -0
  123. package/dist/components/markdown/reading-context.d.ts +18 -0
  124. package/dist/components/markdown/reading-context.js +35 -0
  125. package/dist/components/markdown/render-markdown.d.ts +10 -0
  126. package/dist/components/markdown/render-markdown.js +52 -0
  127. package/dist/components/use-element-width.d.ts +11 -0
  128. package/dist/components/use-element-width.js +38 -0
  129. package/dist/css/chart-tones.d.ts +2 -0
  130. package/dist/css/chart-tones.js +5 -1
  131. package/dist/css/css-article.d.ts +2 -0
  132. package/dist/css/css-article.js +110 -0
  133. package/dist/css/css-bar-chart.d.ts +17 -2
  134. package/dist/css/css-bar-chart.js +38 -7
  135. package/dist/css/css-button-group-attached.d.ts +3 -0
  136. package/dist/css/css-button-group-attached.js +18 -1
  137. package/dist/css/css-canvas-grouped.d.ts +17 -0
  138. package/dist/css/css-canvas-grouped.js +40 -0
  139. package/dist/css/css-components.js +18 -0
  140. package/dist/css/css-count-pill.js +14 -1
  141. package/dist/css/css-date-field-calendar-entry.d.ts +11 -0
  142. package/dist/css/css-date-field-calendar-entry.js +50 -0
  143. package/dist/css/css-date-field.js +4 -0
  144. package/dist/css/css-denomination-grid.js +4 -0
  145. package/dist/css/css-denomination-transposed.d.ts +4 -0
  146. package/dist/css/css-denomination-transposed.js +178 -0
  147. package/dist/css/css-dialog-canvas.d.ts +3 -0
  148. package/dist/css/css-dialog-canvas.js +20 -0
  149. package/dist/css/css-dialog-frame.js +19 -2
  150. package/dist/css/css-dialog-grabber.d.ts +4 -0
  151. package/dist/css/css-dialog-grabber.js +56 -0
  152. package/dist/css/css-dialog.js +3 -2
  153. package/dist/css/css-document-viewer.d.ts +2 -0
  154. package/dist/css/css-document-viewer.js +186 -0
  155. package/dist/css/css-document.js +3 -0
  156. package/dist/css/css-field-width.d.ts +12 -0
  157. package/dist/css/css-field-width.js +49 -0
  158. package/dist/css/css-field.js +4 -0
  159. package/dist/css/css-file-field.d.ts +2 -0
  160. package/dist/css/css-file-field.js +180 -0
  161. package/dist/css/css-filter-chips-layout.d.ts +2 -0
  162. package/dist/css/css-filter-chips-layout.js +42 -0
  163. package/dist/css/css-filter-chips.js +4 -0
  164. package/dist/css/css-list-row-trailing.d.ts +2 -0
  165. package/dist/css/css-list-row-trailing.js +91 -0
  166. package/dist/css/css-list-row.d.ts +6 -0
  167. package/dist/css/css-list-row.js +19 -3
  168. package/dist/css/css-list.js +5 -1
  169. package/dist/css/css-markdown.d.ts +2 -0
  170. package/dist/css/css-markdown.js +144 -0
  171. package/dist/css/css-segmented-sizing.d.ts +13 -0
  172. package/dist/css/css-segmented-sizing.js +66 -0
  173. package/dist/css/css-segmented.js +4 -0
  174. package/dist/css/css-switch.d.ts +6 -0
  175. package/dist/css/css-switch.js +39 -0
  176. package/dist/css/css-variables.js +1 -0
  177. package/dist/css/stacking.d.ts +1 -0
  178. package/dist/css/stacking.js +4 -1
  179. package/dist/css/stylesheet.js +12 -0
  180. package/dist/date-field-labels.d.ts +5 -0
  181. package/dist/date-field-labels.js +1 -0
  182. package/dist/document-viewer-worker.d.ts +9 -0
  183. package/dist/document-viewer-worker.js +9 -0
  184. package/dist/index.d.ts +15 -9
  185. package/dist/index.js +13 -7
  186. package/dist/list-options.d.ts +11 -7
  187. package/dist/list-options.js +20 -8
  188. package/dist/styles.css +889 -44
  189. package/dist/theme/ThemeProvider.d.ts +11 -1
  190. package/dist/theme/ThemeProvider.js +4 -2
  191. package/package.json +10 -2
@@ -0,0 +1,75 @@
1
+ import { type RequiredMark } from '../FieldLabelText.js';
2
+ export type FilterChipOption = {
3
+ value: string;
4
+ label: string;
5
+ count?: number;
6
+ disabled?: boolean;
7
+ };
8
+ /**
9
+ * How a chip's count is drawn: `figure` (the default), a muted tabular
10
+ * figure after a space; `inline`, the label's own words and type, after a
11
+ * middle dot ("Open · 3"), the dot hidden from assistive technology.
12
+ */
13
+ export declare const filterChipCountAppearances: readonly ["figure", "inline"];
14
+ export type FilterChipCountAppearance = (typeof filterChipCountAppearances)[number];
15
+ /**
16
+ * What the chips do when they do not fit their row: `wrap` (the default)
17
+ * onto more rows, or `scroll` sideways on one row.
18
+ */
19
+ export declare const filterChipsOverflows: readonly ["wrap", "scroll"];
20
+ export type FilterChipsOverflow = (typeof filterChipsOverflows)[number];
21
+ /** One chosen chip, a radio in a group. */
22
+ type SingleChoice = {
23
+ multiple?: false;
24
+ /** The chosen option's value, or `''` while nothing is chosen yet. */
25
+ value: string;
26
+ onChange: (value: string) => void;
27
+ /**
28
+ * A choice must be made, such as a reason: each radio is `required` and
29
+ * the label shows the required mark (hidden from assistive technology,
30
+ * which reads the radios' own state).
31
+ */
32
+ required?: boolean;
33
+ };
34
+ /**
35
+ * Several chips on at once, each a checkbox, such as the days of a week.
36
+ * Nothing makes a checkbox group required, so `required` is refused: say
37
+ * what is needed in `error`.
38
+ */
39
+ type MultipleChoice = {
40
+ multiple: true;
41
+ /** The chosen options' values, in any order; empty for none. */
42
+ value: readonly string[];
43
+ /** Every chosen value, in the options' order. */
44
+ onChange: (values: string[]) => void;
45
+ required?: false;
46
+ };
47
+ type FilterChipsChoice = SingleChoice | MultipleChoice;
48
+ export type FilterChipsProps = FilterChipsChoice & {
49
+ label: string;
50
+ options: readonly FilterChipOption[];
51
+ /**
52
+ * Keeps the label as the group's name but does not draw it, for chips
53
+ * whose meaning their words and place already show.
54
+ */
55
+ labelVisuallyHidden?: boolean;
56
+ /**
57
+ * `hidden` leaves out the label's " *" while the control stays required
58
+ * (and announced so); `visible` (default) draws it.
59
+ * When hidden, say in visible text which fields must be filled; a
60
+ * placeholder alone is not enough.
61
+ */
62
+ requiredMark?: RequiredMark;
63
+ countAppearance?: FilterChipCountAppearance;
64
+ overflow?: FilterChipsOverflow;
65
+ /**
66
+ * A message under the chips, such as "Pick a reason": a polite live
67
+ * region linked to the group by `aria-describedby`; the group is marked
68
+ * invalid and every chip takes the danger edge. An empty string is no
69
+ * error.
70
+ */
71
+ error?: string;
72
+ /** More ids that describe the group, such as a hint beside it. */
73
+ 'aria-describedby'?: string;
74
+ };
75
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * How a chip's count is drawn: `figure` (the default), a muted tabular
3
+ * figure after a space; `inline`, the label's own words and type, after a
4
+ * middle dot ("Open · 3"), the dot hidden from assistive technology.
5
+ */
6
+ export const filterChipCountAppearances = ['figure', 'inline'];
7
+ /**
8
+ * What the chips do when they do not fit their row: `wrap` (the default)
9
+ * onto more rows, or `scroll` sideways on one row.
10
+ */
11
+ export const filterChipsOverflows = ['wrap', 'scroll'];
@@ -0,0 +1,9 @@
1
+ import { type FilterChipsProps } from './options.js';
2
+ /**
3
+ * Fails closed on chips it cannot draw: an empty label, no options, blank
4
+ * or repeated values, a chosen value that is no option, a count that is
5
+ * not a whole number from 0, an unknown count appearance or overflow, and
6
+ * a multiple choice asked to be required or given a value that is not a
7
+ * list.
8
+ */
9
+ export declare function assertFilterChips(props: FilterChipsProps): void;
@@ -0,0 +1,48 @@
1
+ import { assertRequiredMark } from '../FieldLabelText.js';
2
+ import { filterChipCountAppearances, filterChipsOverflows, } from './options.js';
3
+ function isCount(count) {
4
+ return count === undefined || (Number.isInteger(count) && count >= 0);
5
+ }
6
+ function chosenValues(props) {
7
+ if (props.multiple)
8
+ return props.value;
9
+ return props.value === '' ? [] : [props.value];
10
+ }
11
+ /**
12
+ * Fails closed on chips it cannot draw: an empty label, no options, blank
13
+ * or repeated values, a chosen value that is no option, a count that is
14
+ * not a whole number from 0, an unknown count appearance or overflow, and
15
+ * a multiple choice asked to be required or given a value that is not a
16
+ * list.
17
+ */
18
+ export function assertFilterChips(props) {
19
+ const { label, options } = props;
20
+ if (!label.trim())
21
+ throw new RangeError('label must not be empty');
22
+ assertRequiredMark(props.requiredMark ?? 'visible');
23
+ if (options.length === 0 ||
24
+ options.some((option) => !option.value.trim() || !option.label.trim()))
25
+ throw new RangeError('options must have nonempty values and labels');
26
+ const values = options.map((option) => option.value);
27
+ if (new Set(values).size !== values.length)
28
+ throw new RangeError('options must have unique values');
29
+ if (props.multiple) {
30
+ if (!Array.isArray(props.value))
31
+ throw new RangeError('a multiple FilterChips value must be a list');
32
+ if (props.required)
33
+ throw new RangeError('a multiple FilterChips cannot be required; say what is needed in error');
34
+ }
35
+ const chosen = chosenValues(props);
36
+ if (chosen.some((value) => !values.includes(value)))
37
+ throw new RangeError(props.multiple
38
+ ? 'every value must match an option'
39
+ : 'value must match an option or be empty');
40
+ if (options.some((option) => !isCount(option.count)))
41
+ throw new RangeError('count must be a non-negative integer');
42
+ const countAppearance = props.countAppearance ?? 'figure';
43
+ if (!filterChipCountAppearances.includes(countAppearance))
44
+ throw new RangeError(`Unknown FilterChips countAppearance: ${String(countAppearance)}`);
45
+ const overflow = props.overflow ?? 'wrap';
46
+ if (!filterChipsOverflows.includes(overflow))
47
+ throw new RangeError(`Unknown FilterChips overflow: ${String(overflow)}`);
48
+ }
@@ -1,4 +1,5 @@
1
1
  import { type ReactNode } from 'react';
2
+ import { type BadgeTone } from '../../css/css-data.js';
2
3
  import { type ListRowAccessory, type ListRowTitleAppearance, type ListRowTone, type ListRowValueTone } from '../../list-options.js';
3
4
  export type ListRowContentProps = {
4
5
  /** Prefixes the ids that name and describe a pressable row. */
@@ -8,13 +9,18 @@ export type ListRowContentProps = {
8
9
  value?: string;
9
10
  valueDetail?: string;
10
11
  valueTone?: ListRowValueTone;
12
+ valueBadge?: BadgeTone;
11
13
  leading?: ReactNode;
12
14
  tone: ListRowTone;
13
15
  titleAppearance: ListRowTitleAppearance;
14
16
  accessory: ListRowAccessory;
17
+ /** The unread words; the dot shows while they are given. */
18
+ unreadLabel?: string;
19
+ tag?: ReactNode;
15
20
  };
16
21
  /**
17
22
  * A list row's parts in reading order: the hidden leading mark, the title
18
- * over its detail, the value over its detail, then the chevron or check.
23
+ * (with the unread dot and its hidden words) over its detail, the value
24
+ * over its detail, the tag, then the chevron or check.
19
25
  */
20
- export declare function ListRowContent({ id, title, detail, value, valueDetail, valueTone, leading, tone, titleAppearance, accessory, }: ListRowContentProps): import("react").JSX.Element;
26
+ export declare function ListRowContent({ id, title, detail, value, valueDetail, valueTone, valueBadge, leading, tone, titleAppearance, accessory, unreadLabel, tag, }: ListRowContentProps): import("react").JSX.Element;
@@ -1,10 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { cx } from '../../class-names.js';
3
+ import { Badge } from '../Badge.js';
3
4
  import { ListRowAccessoryMark } from './ListRowAccessoryMark.js';
5
+ import { hasSlot } from './row-slot.js';
4
6
  /**
5
7
  * A list row's parts in reading order: the hidden leading mark, the title
6
- * over its detail, the value over its detail, then the chevron or check.
8
+ * (with the unread dot and its hidden words) over its detail, the value
9
+ * over its detail, the tag, then the chevron or check.
7
10
  */
8
- export function ListRowContent({ id, title, detail, value, valueDetail, valueTone, leading, tone, titleAppearance, accessory, }) {
9
- return (_jsxs(_Fragment, { children: [leading === undefined ? null : (_jsx("span", { "aria-hidden": "true", className: "sw-list-row-leading", children: leading })), _jsxs("span", { className: "sw-list-row-label", children: [_jsx("span", { className: cx('sw-list-row-title', titleAppearance === 'plain' && 'sw-list-row-title-plain', tone === 'danger' && 'sw-list-row-title-danger'), id: `${id}-title`, children: title }), detail ? (_jsx("span", { className: "sw-list-row-detail", id: `${id}-detail`, children: detail })) : null] }), value === undefined ? null : (_jsxs("span", { className: "sw-list-row-end", children: [_jsx("span", { className: cx('sw-list-row-value', valueTone && `sw-list-row-value-${valueTone}`), id: `${id}-value`, children: value }), valueDetail ? (_jsx("span", { className: "sw-list-row-value-detail", id: `${id}-value-detail`, children: valueDetail })) : null] })), _jsx(ListRowAccessoryMark, { kind: accessory })] }));
11
+ export function ListRowContent({ id, title, detail, value, valueDetail, valueTone, valueBadge, leading, tone, titleAppearance, accessory, unreadLabel, tag, }) {
12
+ return (_jsxs(_Fragment, { children: [!hasSlot(leading) ? null : (_jsx("span", { "aria-hidden": "true", className: "sw-list-row-leading", children: leading })), _jsxs("span", { className: "sw-list-row-label", children: [_jsxs("span", { className: cx('sw-list-row-title', titleAppearance === 'plain' && 'sw-list-row-title-plain', tone === 'danger' && 'sw-list-row-title-danger'), id: `${id}-title`, children: [title, unreadLabel === undefined ? null : (_jsx("span", { "aria-hidden": "true", className: "sw-list-row-unread" }))] }), unreadLabel === undefined ? null : (_jsx("span", { className: "sw-sr-only", id: `${id}-unread`, children: unreadLabel })), detail ? (_jsx("span", { className: "sw-list-row-detail", id: `${id}-detail`, children: detail })) : null] }), value === undefined ? null : (_jsxs("span", { className: "sw-list-row-end", children: [_jsx("span", { className: cx('sw-list-row-value', valueTone && `sw-list-row-value-${valueTone}`), id: `${id}-value`, children: valueBadge ? _jsx(Badge, { tone: valueBadge, children: value }) : value }), valueDetail ? (_jsx("span", { className: "sw-list-row-value-detail", id: `${id}-value-detail`, children: valueDetail })) : null] })), !hasSlot(tag) ? null : (_jsx("span", { className: "sw-list-row-tag", id: `${id}-tag`, children: tag })), _jsx(ListRowAccessoryMark, { kind: accessory })] }));
10
13
  }
@@ -0,0 +1,10 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * A row's list item: an `li` in a ListGroup's list, a `div` elsewhere. A
4
+ * row with an `action` holds that control after the row, a sibling of the
5
+ * row's button or link, so it is never inside the row's press area.
6
+ */
7
+ export declare function ListRowItem({ action, children, }: {
8
+ action: ReactNode;
9
+ children: ReactNode;
10
+ }): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useContext } from 'react';
3
+ import { cx } from '../../class-names.js';
4
+ import { ListRowContainer } from './list-row-container.js';
5
+ import { hasSlot } from './row-slot.js';
6
+ /**
7
+ * A row's list item: an `li` in a ListGroup's list, a `div` elsewhere. A
8
+ * row with an `action` holds that control after the row, a sibling of the
9
+ * row's button or link, so it is never inside the row's press area.
10
+ */
11
+ export function ListRowItem({ action, children, }) {
12
+ const container = useContext(ListRowContainer);
13
+ const hasAction = hasSlot(action);
14
+ const content = (_jsxs(_Fragment, { children: [children, hasAction ? _jsx("div", { className: "sw-list-row-action", children: action }) : null] }));
15
+ const className = cx('sw-list-item', hasAction && 'sw-list-item-action');
16
+ return container === 'li' ? (_jsx("li", { className: className, children: content })) : (_jsx("div", { className: className, children: content }));
17
+ }
@@ -0,0 +1,17 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * A pressable row's name (its title, the unread words and its value) and
4
+ * description (its detail lines and its tag), by the ids its parts carry.
5
+ */
6
+ export declare function rowNaming(id: string, row: {
7
+ 'aria-label'?: string;
8
+ value?: string;
9
+ detail?: string;
10
+ valueDetail?: string;
11
+ unread: boolean;
12
+ tag?: ReactNode;
13
+ }): {
14
+ 'aria-label': string | undefined;
15
+ 'aria-labelledby': string | undefined;
16
+ 'aria-describedby': string | undefined;
17
+ };
@@ -0,0 +1,19 @@
1
+ import { hasSlot } from './row-slot.js';
2
+ function ids(...parts) {
3
+ const joined = parts.filter(Boolean).join(' ');
4
+ return joined === '' ? undefined : joined;
5
+ }
6
+ /**
7
+ * A pressable row's name (its title, the unread words and its value) and
8
+ * description (its detail lines and its tag), by the ids its parts carry.
9
+ */
10
+ export function rowNaming(id, row) {
11
+ const hasTag = hasSlot(row.tag);
12
+ return {
13
+ 'aria-label': row['aria-label'],
14
+ 'aria-labelledby': row['aria-label']
15
+ ? undefined
16
+ : ids(`${id}-title`, row.unread && `${id}-unread`, row.value !== undefined && `${id}-value`),
17
+ 'aria-describedby': ids(Boolean(row.detail) && `${id}-detail`, Boolean(row.valueDetail) && `${id}-value-detail`, hasTag && `${id}-tag`),
18
+ };
19
+ }
@@ -0,0 +1,8 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * Whether a row's slot (`leading`, `tag`, `action`) holds something to
4
+ * draw. As React renders nothing for them, `undefined`, `null`, a boolean
5
+ * (the `false` of `canArchive && <ActionMenu …/>`) and an empty string are
6
+ * an empty slot, so they add no box and no gap.
7
+ */
8
+ export declare function hasSlot(node: ReactNode): boolean;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Whether a row's slot (`leading`, `tag`, `action`) holds something to
3
+ * draw. As React renders nothing for them, `undefined`, `null`, a boolean
4
+ * (the `false` of `canArchive && <ActionMenu …/>`) and an empty string are
5
+ * an empty slot, so they add no box and no gap.
6
+ */
7
+ export function hasSlot(node) {
8
+ return (node !== undefined &&
9
+ node !== null &&
10
+ node !== '' &&
11
+ typeof node !== 'boolean');
12
+ }
@@ -0,0 +1,14 @@
1
+ /** How many quotes and lists the view draws inside one another. */
2
+ export declare const maxNesting = 16;
3
+ /**
4
+ * How deep the parser reads (a quote is one level, a list and its item
5
+ * two). markdown-it stops reading a block that nests deeper (its words are
6
+ * lost, and reported as `limit`). Its work on runs of link and image
7
+ * openers grows with this cap, so it stays low: 128 keeps 60-deep lists
8
+ * and reads 25,000 `![` in about 0.1 s.
9
+ */
10
+ export declare const maxParseNesting = 128;
11
+ /** The longest excerpt an omission keeps. */
12
+ export declare const maxExcerpt = 80;
13
+ /** How many omissions the reading lists; the counts keep the rest. */
14
+ export declare const maxOmissionsListed = 100;
@@ -0,0 +1,14 @@
1
+ /** How many quotes and lists the view draws inside one another. */
2
+ export const maxNesting = 16;
3
+ /**
4
+ * How deep the parser reads (a quote is one level, a list and its item
5
+ * two). markdown-it stops reading a block that nests deeper (its words are
6
+ * lost, and reported as `limit`). Its work on runs of link and image
7
+ * openers grows with this cap, so it stays low: 128 keeps 60-deep lists
8
+ * and reads 25,000 `![` in about 0.1 s.
9
+ */
10
+ export const maxParseNesting = 128;
11
+ /** The longest excerpt an omission keeps. */
12
+ export const maxExcerpt = 80;
13
+ /** How many omissions the reading lists; the counts keep the rest. */
14
+ export const maxOmissionsListed = 100;
@@ -0,0 +1,94 @@
1
+ /** A run of text and its marks, as the view draws it. */
2
+ export type MarkdownInline = {
3
+ kind: 'text';
4
+ value: string;
5
+ } | {
6
+ kind: 'strong' | 'emphasis';
7
+ children: MarkdownInline[];
8
+ } | {
9
+ kind: 'link';
10
+ href: string;
11
+ children: MarkdownInline[];
12
+ } | {
13
+ kind: 'break';
14
+ };
15
+ /** A block of the page, as the view draws it. */
16
+ export type MarkdownBlock = {
17
+ kind: 'paragraph';
18
+ children: MarkdownInline[];
19
+ } | {
20
+ kind: 'heading';
21
+ level: 1 | 2 | 3;
22
+ id: string;
23
+ children: MarkdownInline[];
24
+ } | {
25
+ kind: 'list';
26
+ ordered: boolean;
27
+ start: number | null;
28
+ tight: boolean;
29
+ items: MarkdownBlock[][];
30
+ } | {
31
+ kind: 'quote';
32
+ children: MarkdownBlock[];
33
+ } | {
34
+ kind: 'figure';
35
+ src: string;
36
+ alt: string;
37
+ caption: string;
38
+ } | {
39
+ kind: 'rule';
40
+ };
41
+ /** A heading of the page, for an "On this page" list. */
42
+ export type MarkdownHeading = {
43
+ /** The heading's element id, for a link to it (`#${id}`). */
44
+ id: string;
45
+ /** 1 for `#`, 2 for `##`, 3 for `###` (and deeper). */
46
+ level: 1 | 2 | 3;
47
+ text: string;
48
+ };
49
+ /** What the view leaves out of the Markdown it is given. */
50
+ export type MarkdownOmissionKind =
51
+ /** Raw HTML, scripts included: dropped (the words between tags stay). */
52
+ 'html'
53
+ /** A table: dropped. */
54
+ | 'table'
55
+ /** An image that is not a `photo:` the consumer resolves: dropped. */
56
+ | 'image'
57
+ /** A link whose scheme is not http, https or mailto: shown as its words. */
58
+ | 'link'
59
+ /** Code: shown as plain text, without its code formatting. */
60
+ | 'code'
61
+ /**
62
+ * Past the view's limits: quotes, lists and marks nested more than 16
63
+ * deep are shown as their words (a link there too, quoted in the run's
64
+ * one omission); a block reaching the parser's 128th level, past which
65
+ * it stops reading, is reported as it may be lost.
66
+ */
67
+ | 'limit';
68
+ export declare const markdownOmissionKinds: readonly ["html", "table", "image", "link", "code", "limit"];
69
+ /** How many of each kind the view left out, the ones past the list too. */
70
+ export type MarkdownOmissionCounts = Record<MarkdownOmissionKind, number>;
71
+ export type MarkdownOmission = {
72
+ kind: MarkdownOmissionKind;
73
+ /** The Markdown it came from, cut to 80 characters. */
74
+ source: string;
75
+ };
76
+ /** A `photo:` image as the consumer resolves it. */
77
+ export type MarkdownImage = {
78
+ /**
79
+ * The photo's address: relative, `https:`, `http:`, `blob:` or a
80
+ * `data:image/` of PNG, JPEG, GIF, WebP, AVIF or SVG; any other is left out.
81
+ */
82
+ src: string;
83
+ /** What the photo shows; the caption is used when this is left out. */
84
+ alt?: string;
85
+ };
86
+ export type MarkdownImageResolver = (id: string) => MarkdownImage | null;
87
+ /** The page read from Markdown: its blocks, its headings, what was left out. */
88
+ export type MarkdownReading = {
89
+ blocks: MarkdownBlock[];
90
+ headings: MarkdownHeading[];
91
+ /** The first 100 things left out, in the order they appear. */
92
+ omissions: MarkdownOmission[];
93
+ omissionCounts: MarkdownOmissionCounts;
94
+ };
@@ -0,0 +1,8 @@
1
+ export const markdownOmissionKinds = [
2
+ 'html',
3
+ 'table',
4
+ 'image',
5
+ 'link',
6
+ 'code',
7
+ 'limit',
8
+ ];
@@ -0,0 +1,18 @@
1
+ import { type MarkdownImageResolver } from './markdown-model.js';
2
+ export type MarkdownOptions = {
3
+ /**
4
+ * Turns `![caption](photo:<id>)` into a photo: its `src` and its `alt`
5
+ * (the caption when left out); null leaves it out. Any other image is
6
+ * left out. A new function reads the source again, so keep it stable
7
+ * (`useCallback`) where the view renders often.
8
+ */
9
+ resolveImage?: MarkdownImageResolver;
10
+ /**
11
+ * Put before every heading id, such as "page-", so its ids never clash
12
+ * with the page's or another view's (`''`, the default, is none). Pass
13
+ * the same one to the view and to `inspectMarkdown`.
14
+ */
15
+ idPrefix?: string;
16
+ };
17
+ /** Refuses what neither the view nor `inspectMarkdown` can read. */
18
+ export declare function assertMarkdownInput(owner: string, source: unknown, { resolveImage, idPrefix }: MarkdownOptions): void;
@@ -0,0 +1,10 @@
1
+ /** Refuses what neither the view nor `inspectMarkdown` can read. */
2
+ export function assertMarkdownInput(owner, source, { resolveImage, idPrefix }) {
3
+ if (typeof source !== 'string')
4
+ throw new RangeError(`${owner} source must be text`);
5
+ if (resolveImage !== undefined && typeof resolveImage !== 'function')
6
+ throw new RangeError(`${owner} resolveImage must be a function`);
7
+ if (idPrefix !== undefined &&
8
+ (typeof idPrefix !== 'string' || !/^([A-Za-z][\w-]*)?$/.test(idPrefix)))
9
+ throw new RangeError(`${owner} idPrefix must be empty or start with a letter and hold letters, digits, - or _`);
10
+ }
@@ -0,0 +1,28 @@
1
+ /** The link schemes a page may use; any other is shown as plain words. */
2
+ export declare const markdownLinkSchemes: readonly ["http:", "https:", "mailto:"];
3
+ /**
4
+ * The address a link may open, normalised as the browser reads it, or null
5
+ * for any scheme but http, https and mailto (`javascript:`, `data:`, a
6
+ * relative or protocol-relative address, anything that is not a URL).
7
+ */
8
+ export declare function safeLinkHref(url: string): string | null;
9
+ /** The id in `photo:<id>`, or null for any other image address. */
10
+ export declare function photoId(url: string): string | null;
11
+ /**
12
+ * Whether a resolved photo's address may be drawn: relative, `https:`,
13
+ * `http:`, `blob:` or a `data:` PNG, JPEG, GIF, WebP, AVIF or SVG (an
14
+ * `img` never runs an SVG's script); never any other scheme.
15
+ */
16
+ export declare function isSafeImageSrc(src: string): boolean;
17
+ /**
18
+ * Heading ids for one document: from a heading's words, `section` when
19
+ * nothing is left, after `prefix`; a repeat takes the next free `-2`,
20
+ * `-3`. Each base remembers its next number, so many repeats stay linear.
21
+ */
22
+ export declare class HeadingIds {
23
+ private readonly prefix;
24
+ private readonly taken;
25
+ private readonly next;
26
+ constructor(prefix: string);
27
+ take(text: string): string;
28
+ }
@@ -0,0 +1,81 @@
1
+ /** The link schemes a page may use; any other is shown as plain words. */
2
+ export const markdownLinkSchemes = ['http:', 'https:', 'mailto:'];
3
+ /**
4
+ * The address a link may open, normalised as the browser reads it, or null
5
+ * for any scheme but http, https and mailto (`javascript:`, `data:`, a
6
+ * relative or protocol-relative address, anything that is not a URL).
7
+ */
8
+ export function safeLinkHref(url) {
9
+ let parsed;
10
+ try {
11
+ parsed = new URL(url);
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ return markdownLinkSchemes.includes(parsed.protocol)
17
+ ? parsed.href
18
+ : null;
19
+ }
20
+ /** The id in `photo:<id>`, or null for any other image address. */
21
+ export function photoId(url) {
22
+ const match = /^photo:([A-Za-z0-9_-]{1,64})$/.exec(url.trim());
23
+ return match ? match[1] : null;
24
+ }
25
+ const imageSchemes = ['http:', 'https:', 'blob:'];
26
+ const imageData = /^data:image\/(png|jpeg|gif|webp|avif|svg\+xml)[;,]/i;
27
+ /**
28
+ * Whether a resolved photo's address may be drawn: relative, `https:`,
29
+ * `http:`, `blob:` or a `data:` PNG, JPEG, GIF, WebP, AVIF or SVG (an
30
+ * `img` never runs an SVG's script); never any other scheme.
31
+ */
32
+ export function isSafeImageSrc(src) {
33
+ if (typeof src !== 'string' || src.trim() === '')
34
+ return false;
35
+ let parsed;
36
+ try {
37
+ parsed = new URL(src, 'https://relative.invalid/');
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ if (parsed.protocol === 'data:')
43
+ return imageData.test(src.trim());
44
+ return imageSchemes.includes(parsed.protocol);
45
+ }
46
+ /** A heading's words as an id: lower case, accents dropped, hyphens. */
47
+ function slug(text) {
48
+ return text
49
+ .normalize('NFKD')
50
+ .replace(/[\u0300-\u036f]/g, '')
51
+ .toLowerCase()
52
+ .replace(/[^a-z0-9]+/g, '-')
53
+ .replace(/^-+|-+$/g, '');
54
+ }
55
+ /**
56
+ * Heading ids for one document: from a heading's words, `section` when
57
+ * nothing is left, after `prefix`; a repeat takes the next free `-2`,
58
+ * `-3`. Each base remembers its next number, so many repeats stay linear.
59
+ */
60
+ export class HeadingIds {
61
+ prefix;
62
+ taken = new Set();
63
+ next = new Map();
64
+ constructor(prefix) {
65
+ this.prefix = prefix;
66
+ }
67
+ take(text) {
68
+ const base = `${this.prefix}${slug(text) || 'section'}`;
69
+ let id = base;
70
+ let count = this.next.get(base) ?? 2;
71
+ if (this.taken.has(id)) {
72
+ do {
73
+ id = `${base}-${count}`;
74
+ count += 1;
75
+ } while (this.taken.has(id));
76
+ }
77
+ this.next.set(base, count);
78
+ this.taken.add(id);
79
+ return id;
80
+ }
81
+ }
@@ -0,0 +1,17 @@
1
+ import { type MarkdownInline } from './markdown-model.js';
2
+ import { type Token } from './parse-markdown.js';
3
+ import { type ReadingContext } from './reading-context.js';
4
+ /** Left-out text as an omission quotes it: trimmed, at most `maxExcerpt`. */
5
+ export declare function excerpt(text: string): string;
6
+ /** A run's words without its marks. */
7
+ export declare function plainText(inlines: readonly MarkdownInline[]): string;
8
+ /**
9
+ * The words of `tokens` (block tokens, or one block's inline children),
10
+ * in order, at any depth: a flat walk, so no nesting overflows the stack.
11
+ * Raw HTML, tables and images are never words: each is noted as left out.
12
+ */
13
+ export declare function tokenText(context: ReadingContext, tokens: readonly Token[]): string;
14
+ /** Code shown as plain lines, a break between each. */
15
+ export declare function lines(value: string): MarkdownInline[];
16
+ /** Drops the spaces an image left at a paragraph part's edges. */
17
+ export declare function trimEdges(inlines: MarkdownInline[]): MarkdownInline[];