@scalewing/react 1.41.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 (100) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +120 -1
  3. package/dist/components/Article.d.ts +60 -0
  4. package/dist/components/Article.js +42 -0
  5. package/dist/components/DocumentViewer.d.ts +41 -0
  6. package/dist/components/DocumentViewer.js +72 -0
  7. package/dist/components/FileField.d.ts +44 -0
  8. package/dist/components/FileField.js +66 -0
  9. package/dist/components/ListRow.d.ts +23 -2
  10. package/dist/components/ListRow.js +17 -17
  11. package/dist/components/MarkdownView.d.ts +39 -0
  12. package/dist/components/MarkdownView.js +30 -0
  13. package/dist/components/NavCount.d.ts +15 -2
  14. package/dist/components/NavCount.js +16 -6
  15. package/dist/components/chart/use-chart-width.js +2 -24
  16. package/dist/components/document-viewer/document-viewer-labels.d.ts +23 -0
  17. package/dist/components/document-viewer/document-viewer-labels.js +23 -0
  18. package/dist/components/document-viewer/page-box.d.ts +8 -0
  19. package/dist/components/document-viewer/page-box.js +12 -0
  20. package/dist/components/document-viewer/pdf-document.d.ts +32 -0
  21. package/dist/components/document-viewer/pdf-document.js +66 -0
  22. package/dist/components/document-viewer/pdf-options.d.ts +36 -0
  23. package/dist/components/document-viewer/pdf-options.js +46 -0
  24. package/dist/components/document-viewer/render-pdf-page.d.ts +20 -0
  25. package/dist/components/document-viewer/render-pdf-page.js +38 -0
  26. package/dist/components/document-viewer/scroll-chain.d.ts +6 -0
  27. package/dist/components/document-viewer/scroll-chain.js +23 -0
  28. package/dist/components/document-viewer/use-error-report.d.ts +5 -0
  29. package/dist/components/document-viewer/use-error-report.js +13 -0
  30. package/dist/components/document-viewer/use-page-render.d.ts +21 -0
  31. package/dist/components/document-viewer/use-page-render.js +70 -0
  32. package/dist/components/document-viewer/use-pdf-document.d.ts +22 -0
  33. package/dist/components/document-viewer/use-pdf-document.js +47 -0
  34. package/dist/components/document-viewer/use-pinch-zoom.d.ts +12 -0
  35. package/dist/components/document-viewer/use-pinch-zoom.js +67 -0
  36. package/dist/components/document-viewer/use-zoom.d.ts +23 -0
  37. package/dist/components/document-viewer/use-zoom.js +70 -0
  38. package/dist/components/document-viewer/zoom.d.ts +29 -0
  39. package/dist/components/document-viewer/zoom.js +37 -0
  40. package/dist/components/file-field/file-field-checks.d.ts +13 -0
  41. package/dist/components/file-field/file-field-checks.js +21 -0
  42. package/dist/components/file-field/use-file-drop.d.ts +34 -0
  43. package/dist/components/file-field/use-file-drop.js +78 -0
  44. package/dist/components/file-field/use-swap-focus.d.ts +15 -0
  45. package/dist/components/file-field/use-swap-focus.js +40 -0
  46. package/dist/components/list/ListRowContent.d.ts +6 -2
  47. package/dist/components/list/ListRowContent.js +5 -3
  48. package/dist/components/list/ListRowItem.d.ts +10 -0
  49. package/dist/components/list/ListRowItem.js +17 -0
  50. package/dist/components/list/row-naming.d.ts +17 -0
  51. package/dist/components/list/row-naming.js +19 -0
  52. package/dist/components/list/row-slot.d.ts +8 -0
  53. package/dist/components/list/row-slot.js +12 -0
  54. package/dist/components/markdown/markdown-limits.d.ts +14 -0
  55. package/dist/components/markdown/markdown-limits.js +14 -0
  56. package/dist/components/markdown/markdown-model.d.ts +94 -0
  57. package/dist/components/markdown/markdown-model.js +8 -0
  58. package/dist/components/markdown/markdown-options.d.ts +18 -0
  59. package/dist/components/markdown/markdown-options.js +10 -0
  60. package/dist/components/markdown/markdown-rules.d.ts +28 -0
  61. package/dist/components/markdown/markdown-rules.js +81 -0
  62. package/dist/components/markdown/markdown-text.d.ts +17 -0
  63. package/dist/components/markdown/markdown-text.js +86 -0
  64. package/dist/components/markdown/parse-markdown.d.ts +4 -0
  65. package/dist/components/markdown/parse-markdown.js +21 -0
  66. package/dist/components/markdown/read-blocks.d.ts +8 -0
  67. package/dist/components/markdown/read-blocks.js +168 -0
  68. package/dist/components/markdown/read-inlines.d.ts +12 -0
  69. package/dist/components/markdown/read-inlines.js +126 -0
  70. package/dist/components/markdown/read-markdown.d.ts +9 -0
  71. package/dist/components/markdown/read-markdown.js +27 -0
  72. package/dist/components/markdown/reading-context.d.ts +18 -0
  73. package/dist/components/markdown/reading-context.js +35 -0
  74. package/dist/components/markdown/render-markdown.d.ts +10 -0
  75. package/dist/components/markdown/render-markdown.js +52 -0
  76. package/dist/components/use-element-width.d.ts +11 -0
  77. package/dist/components/use-element-width.js +38 -0
  78. package/dist/css/css-article.d.ts +2 -0
  79. package/dist/css/css-article.js +110 -0
  80. package/dist/css/css-components.js +12 -0
  81. package/dist/css/css-count-pill.js +14 -1
  82. package/dist/css/css-document-viewer.d.ts +2 -0
  83. package/dist/css/css-document-viewer.js +186 -0
  84. package/dist/css/css-file-field.d.ts +2 -0
  85. package/dist/css/css-file-field.js +180 -0
  86. package/dist/css/css-list-row-trailing.d.ts +2 -0
  87. package/dist/css/css-list-row-trailing.js +91 -0
  88. package/dist/css/css-list.js +5 -1
  89. package/dist/css/css-markdown.d.ts +2 -0
  90. package/dist/css/css-markdown.js +144 -0
  91. package/dist/css/css-variables.js +1 -0
  92. package/dist/css/stylesheet.js +8 -0
  93. package/dist/document-viewer-worker.d.ts +9 -0
  94. package/dist/document-viewer-worker.js +9 -0
  95. package/dist/index.d.ts +5 -1
  96. package/dist/index.js +5 -1
  97. package/dist/list-options.d.ts +5 -2
  98. package/dist/list-options.js +7 -2
  99. package/dist/styles.css +504 -0
  100. package/package.json +10 -2
@@ -0,0 +1,186 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { breakpointQuery } from './breakpoints.js';
3
+ import { quietFill } from './quiet-fill.js';
4
+ const label = typographyVariants.label;
5
+ /** How much accent tints a selection of the page's text, in percent. */
6
+ const selectionTint = 30;
7
+ /*
8
+ * The viewer: a quiet grey stage at the large radius holding the page,
9
+ * and under it the page steps around "Page n of m". The stage scrolls
10
+ * sideways when the page is zoomed past its width; it only pans, so two
11
+ * fingers zoom the page, not the screen. The page is centred in it with a
12
+ * space-5 margin (space-3 on a phone), on the surface with the small
13
+ * elevation until PDF.js paints it.
14
+ */
15
+ function stageRules() {
16
+ return `.sw-document-viewer {
17
+ background: ${quietFill};
18
+ border-radius: var(--sw-radius-lg);
19
+ display: flex;
20
+ flex-direction: column;
21
+ min-width: 0;
22
+ padding-block-end: var(--sw-space-4);
23
+ }
24
+
25
+ .sw-document-viewer-stage {
26
+ box-sizing: border-box;
27
+ min-width: 0;
28
+ overflow-x: auto;
29
+ overscroll-behavior-x: contain;
30
+ padding: var(--sw-space-5);
31
+ touch-action: pan-x pan-y;
32
+ }
33
+
34
+ @media ${breakpointQuery('below', 'md')} {
35
+ .sw-document-viewer-stage { padding: var(--sw-space-3); }
36
+ }
37
+
38
+ .sw-document-viewer-stage:focus { outline: none; }
39
+
40
+ .sw-document-viewer-stage:focus-visible {
41
+ border-radius: var(--sw-radius-lg);
42
+ outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
43
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
44
+ }
45
+
46
+ .sw-document-viewer-page {
47
+ aspect-ratio: 1 / var(--sw-document-page-aspect);
48
+ background: var(--sw-color-surface);
49
+ box-shadow: var(--sw-elevation-sm);
50
+ margin-inline: auto;
51
+ position: relative;
52
+ width: var(--sw-document-page-width);
53
+ }
54
+
55
+ .sw-document-viewer-page > canvas {
56
+ display: block;
57
+ height: 100%;
58
+ width: 100%;
59
+ }`;
60
+ }
61
+ /*
62
+ * The page's text, laid over the drawing by PDF.js's TextLayer: each run
63
+ * transparent at its place and size, so it is read by assistive
64
+ * technology and selected, never seen. These are the rules PDF.js's own
65
+ * viewer gives its text layer, at the scale the viewer sets
66
+ * (--total-scale-factor) with whole-pixel rounding; a selection shows in a
67
+ * tint of the accent.
68
+ */
69
+ function textLayerRules() {
70
+ return `.sw-document-viewer-text {
71
+ --scale-round-x: 1px;
72
+ --scale-round-y: 1px;
73
+ --min-font-size: 1;
74
+ --text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size));
75
+ --min-font-size-inv: calc(1 / var(--min-font-size));
76
+ forced-color-adjust: none;
77
+ inset: 0;
78
+ letter-spacing: normal;
79
+ line-height: 1;
80
+ overflow: clip;
81
+ position: absolute;
82
+ text-align: initial;
83
+ text-size-adjust: none;
84
+ transform-origin: 0 0;
85
+ word-spacing: normal;
86
+ }
87
+
88
+ .sw-document-viewer-text :is(span, br) {
89
+ color: transparent;
90
+ cursor: text;
91
+ position: absolute;
92
+ transform-origin: 0% 0%;
93
+ user-select: text;
94
+ white-space: pre;
95
+ }
96
+
97
+ .sw-document-viewer-text > :not(.markedContent),
98
+ .sw-document-viewer-text .markedContent span:not(.markedContent) {
99
+ --font-height: 0;
100
+ --scale-x: 1;
101
+ --rotate: 0deg;
102
+ font-size: calc(var(--text-scale-factor) * var(--font-height));
103
+ transform: rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv));
104
+ }
105
+
106
+ .sw-document-viewer-text .markedContent { display: contents; }
107
+
108
+ .sw-document-viewer-text ::selection {
109
+ background: color-mix(in srgb, var(--sw-color-accent) ${selectionTint}%, transparent);
110
+ color: transparent;
111
+ }`;
112
+ }
113
+ /*
114
+ * The steps are 48-point round neutral Buttons (spacing step 8) with the
115
+ * pager's private chevron, around the page words in label type; with one
116
+ * page only the words show. Loading and a failed page centre their words
117
+ * (and the consumer's actions) in the stage.
118
+ */
119
+ function barRules() {
120
+ return `.sw-document-viewer-bar {
121
+ align-items: center;
122
+ display: flex;
123
+ gap: var(--sw-space-3);
124
+ justify-content: center;
125
+ padding-inline: var(--sw-space-3);
126
+ }
127
+
128
+ .sw-button.sw-document-viewer-step {
129
+ border-radius: var(--sw-radius-pill);
130
+ flex: none;
131
+ min-height: var(--sw-space-8);
132
+ min-width: var(--sw-space-8);
133
+ padding-inline: 0;
134
+ }
135
+
136
+ .sw-document-viewer-status {
137
+ font-family: var(--sw-font-sans);
138
+ font-size: ${label.fontSize}px;
139
+ font-variant-numeric: tabular-nums;
140
+ line-height: ${label.lineHeight}px;
141
+ text-align: center;
142
+ }
143
+
144
+ .sw-document-viewer-message {
145
+ align-items: center;
146
+ display: flex;
147
+ flex-direction: column;
148
+ gap: var(--sw-space-2);
149
+ justify-content: center;
150
+ min-height: calc(4 * var(--sw-space-8));
151
+ text-align: center;
152
+ }
153
+
154
+ .sw-document-viewer-actions {
155
+ display: flex;
156
+ flex-wrap: wrap;
157
+ gap: var(--sw-space-2);
158
+ justify-content: center;
159
+ margin-block-start: var(--sw-space-2);
160
+ }
161
+
162
+ @media (forced-colors: active) {
163
+ .sw-document-viewer { border: 1px solid CanvasText; }
164
+ .sw-document-viewer-page { outline: 1px solid CanvasText; }
165
+ }`;
166
+ }
167
+ export function cssDocumentViewerClasses() {
168
+ return `${stageRules()}
169
+
170
+ ${textLayerRules()}
171
+
172
+ ${barRules()}`;
173
+ }
174
+ export function documentViewerClassCatalog() {
175
+ return [
176
+ 'sw-document-viewer',
177
+ 'sw-document-viewer-stage',
178
+ 'sw-document-viewer-page',
179
+ 'sw-document-viewer-text',
180
+ 'sw-document-viewer-bar',
181
+ 'sw-document-viewer-step',
182
+ 'sw-document-viewer-status',
183
+ 'sw-document-viewer-message',
184
+ 'sw-document-viewer-actions',
185
+ ];
186
+ }
@@ -0,0 +1,2 @@
1
+ export declare function cssFileFieldClasses(): string;
2
+ export declare function fileFieldClassCatalog(): string[];
@@ -0,0 +1,180 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { disabledControlSurface } from './css-document.js';
3
+ import { coarsePointerQuery, touchTarget } from './touch-target.js';
4
+ const label = typographyVariants.label;
5
+ const caption = typographyVariants.caption;
6
+ /*
7
+ * The frame holds the field's one file: empty, a wide zone on the subtle
8
+ * fill that opens the picker (the choose words in the text colour at
9
+ * label weight, as the accent falls under 4.5:1 on the subtle fill in
10
+ * some palettes, the drop words muted after them); chosen, a row on the surface
11
+ * with the consumer's mark, the file's name over its detail and the
12
+ * change button at the end. Both are a hairline frame at the medium
13
+ * radius. A file dragged over either draws the accent edge and tint. A
14
+ * chosen row too narrow for a long name and the button wraps the button
15
+ * under the name, at the row's end.
16
+ */
17
+ function frameRules() {
18
+ return `.sw-file-field-frame {
19
+ border: 1px solid var(--sw-color-border);
20
+ border-radius: var(--sw-radius-md);
21
+ box-sizing: border-box;
22
+ min-width: 0;
23
+ }
24
+
25
+ .sw-file-field-empty { background: var(--sw-color-subtle); }
26
+
27
+ .sw-file-field-chosen {
28
+ align-items: center;
29
+ background: var(--sw-color-surface);
30
+ display: flex;
31
+ flex-wrap: wrap;
32
+ gap: var(--sw-space-2) var(--sw-space-3);
33
+ padding: var(--sw-space-2) var(--sw-space-2) var(--sw-space-2) var(--sw-space-4);
34
+ }
35
+
36
+ .sw-field-invalid .sw-file-field-frame { border-color: var(--sw-color-danger); }
37
+
38
+ /* A drag wins over an error's edge: it says where the file goes. */
39
+ .sw-file-field-frame.sw-file-field-dragging {
40
+ background: var(--sw-color-accentSubtle);
41
+ border-color: var(--sw-color-accent);
42
+ outline: 1px solid var(--sw-color-accent);
43
+ }
44
+
45
+ /* On the tint, muted words fall under 4.5:1 in light palettes: every word takes the text colour. */
46
+ .sw-file-field-dragging :is(.sw-file-field-choose, .sw-file-field-drop, .sw-file-field-detail, .sw-file-field-icon) {
47
+ color: var(--sw-color-text);
48
+ }
49
+
50
+ /* Disabled, the frame's hairline is dashed, as a disabled text control's. */
51
+ .sw-file-field-disabled { border-style: dashed; }`;
52
+ }
53
+ /*
54
+ * The empty zone is one button filling the frame, a space-6 of padding
55
+ * above and below its words, its focus ring inside the frame's edge. On a
56
+ * coarse pointer, where nothing is dragged, the drop words are left out.
57
+ */
58
+ function zoneRules() {
59
+ return `.sw-file-field-zone {
60
+ align-items: center;
61
+ appearance: none;
62
+ background: transparent;
63
+ border: 0;
64
+ border-radius: inherit;
65
+ box-sizing: border-box;
66
+ color: var(--sw-color-text);
67
+ cursor: pointer;
68
+ display: flex;
69
+ flex-wrap: wrap;
70
+ font-family: var(--sw-font-sans);
71
+ font-size: ${label.fontSize}px;
72
+ gap: var(--sw-space-1);
73
+ justify-content: center;
74
+ line-height: ${label.lineHeight}px;
75
+ margin: 0;
76
+ min-height: ${touchTarget};
77
+ padding: var(--sw-space-6) var(--sw-space-4);
78
+ text-align: center;
79
+ width: 100%;
80
+ }
81
+
82
+ .sw-file-field-zone:focus { outline: none; }
83
+
84
+ .sw-file-field-zone:focus-visible {
85
+ outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
86
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
87
+ }
88
+
89
+ .sw-file-field-choose {
90
+ color: var(--sw-color-text);
91
+ font-weight: ${label.fontWeight};
92
+ }
93
+
94
+ .sw-file-field-drop { color: var(--sw-color-muted); }
95
+
96
+ .sw-file-field-zone:disabled {
97
+ ${disabledControlSurface}
98
+ border-radius: inherit;
99
+ }
100
+
101
+ .sw-file-field-zone:disabled .sw-file-field-choose { color: var(--sw-color-muted); }
102
+
103
+ @media ${coarsePointerQuery} {
104
+ .sw-file-field-drop { display: none; }
105
+ }`;
106
+ }
107
+ /*
108
+ * The chosen file: the mark, then the name over its detail (at least four
109
+ * space-8 wide before the button wraps under it), then the button.
110
+ */
111
+ function fileRules() {
112
+ return `.sw-file-field-icon {
113
+ align-items: center;
114
+ color: var(--sw-color-muted);
115
+ display: flex;
116
+ flex: none;
117
+ }
118
+
119
+ .sw-file-field-file {
120
+ display: flex;
121
+ flex: 1 1 calc(4 * var(--sw-space-8));
122
+ flex-direction: column;
123
+ min-width: 0;
124
+ }
125
+
126
+ .sw-file-field-name {
127
+ font-size: ${label.fontSize}px;
128
+ font-weight: ${label.fontWeight};
129
+ line-height: ${label.lineHeight}px;
130
+ overflow-wrap: anywhere;
131
+ }
132
+
133
+ .sw-file-field-detail {
134
+ color: var(--sw-color-muted);
135
+ font-size: ${caption.fontSize}px;
136
+ line-height: ${caption.lineHeight}px;
137
+ }
138
+
139
+ .sw-file-field-chosen > .sw-button {
140
+ flex: none;
141
+ margin-inline-start: auto;
142
+ }`;
143
+ }
144
+ /* Forced colours: system edges, Highlight for a drag, the button text colour for the words. */
145
+ function forcedColorRules() {
146
+ return `@media (forced-colors: active) {
147
+ .sw-file-field-frame { border-color: CanvasText; }
148
+ .sw-field-invalid .sw-file-field-frame { border-color: Mark; }
149
+ .sw-file-field-frame.sw-file-field-dragging { border-color: Highlight; outline-color: Highlight; }
150
+ .sw-file-field-choose { color: ButtonText; }
151
+ .sw-file-field-zone:disabled,
152
+ .sw-file-field-zone:disabled .sw-file-field-choose { color: GrayText; }
153
+ }`;
154
+ }
155
+ export function cssFileFieldClasses() {
156
+ return `${frameRules()}
157
+
158
+ ${zoneRules()}
159
+
160
+ ${fileRules()}
161
+
162
+ ${forcedColorRules()}`;
163
+ }
164
+ export function fileFieldClassCatalog() {
165
+ return [
166
+ 'sw-file-field',
167
+ 'sw-file-field-frame',
168
+ 'sw-file-field-empty',
169
+ 'sw-file-field-chosen',
170
+ 'sw-file-field-dragging',
171
+ 'sw-file-field-disabled',
172
+ 'sw-file-field-zone',
173
+ 'sw-file-field-choose',
174
+ 'sw-file-field-drop',
175
+ 'sw-file-field-icon',
176
+ 'sw-file-field-file',
177
+ 'sw-file-field-name',
178
+ 'sw-file-field-detail',
179
+ ];
180
+ }
@@ -0,0 +1,2 @@
1
+ export declare function cssListRowTrailingClasses(): string;
2
+ export declare function listRowTrailingClassCatalog(): string[];
@@ -0,0 +1,91 @@
1
+ import { quietFill } from './quiet-fill.js';
2
+ /*
3
+ * The unread dot: an accent circle a space-2 across after the title's last
4
+ * word, on its line. It is drawn as a border, so forced colours keep it
5
+ * (in the text colour); the hidden words beside the title say what it is.
6
+ * Centred on the words' middle, as an inline mark.
7
+ */
8
+ function unreadRules() {
9
+ return `.sw-list-row-unread {
10
+ border: calc(var(--sw-space-2) / 2) solid var(--sw-color-accent);
11
+ border-radius: var(--sw-radius-pill);
12
+ box-sizing: border-box;
13
+ display: inline-block;
14
+ height: var(--sw-space-2);
15
+ margin-inline-start: var(--sw-space-2);
16
+ vertical-align: middle;
17
+ width: var(--sw-space-2);
18
+ }
19
+
20
+ @media (forced-colors: active) {
21
+ .sw-list-row-unread { border-color: CanvasText; }
22
+ }`;
23
+ }
24
+ /*
25
+ * The tag (a Badge, "Only in English") sits before the accessory, centred
26
+ * on the row's height and never shrunk or wrapped.
27
+ */
28
+ function tagRules() {
29
+ return `.sw-list-row-tag {
30
+ align-self: center;
31
+ display: inline-flex;
32
+ flex: none;
33
+ white-space: nowrap;
34
+ }`;
35
+ }
36
+ /*
37
+ * A row with an action is the row's button or link followed by the
38
+ * action's control in one list item, the control centred on the row's
39
+ * height after it and outside its press area. The row ends a space-2
40
+ * before the control, and the control keeps the row's inline end padding
41
+ * after it (a space-4 in a panel, none in a plain list). Where :has() is
42
+ * supported, the hover fill spans the whole item, so the row and its
43
+ * control read as one line; elsewhere the row keeps its own fill.
44
+ */
45
+ function actionRules() {
46
+ return `.sw-list-item-action {
47
+ align-items: center;
48
+ display: flex;
49
+ }
50
+
51
+ .sw-list-item-action > .sw-list-row {
52
+ flex: 1 1 auto;
53
+ min-width: 0;
54
+ padding-inline-end: var(--sw-space-2);
55
+ }
56
+
57
+ .sw-list-row-action {
58
+ display: inline-flex;
59
+ flex: none;
60
+ padding-inline-end: var(--sw-space-4);
61
+ }
62
+
63
+ .sw-list-group-plain .sw-list-row-action { padding-inline-end: 0; }
64
+
65
+ @supports selector(:has(*)) {
66
+ @media (hover: hover) {
67
+ .sw-list-item-action:has(> .sw-list-row-pressable:hover:not(.sw-list-row-disabled)) {
68
+ background: ${quietFill};
69
+ }
70
+
71
+ .sw-list-item-action > .sw-list-row-pressable:hover:not(.sw-list-row-disabled) {
72
+ background: transparent;
73
+ }
74
+ }
75
+ }`;
76
+ }
77
+ export function cssListRowTrailingClasses() {
78
+ return `${unreadRules()}
79
+
80
+ ${tagRules()}
81
+
82
+ ${actionRules()}`;
83
+ }
84
+ export function listRowTrailingClassCatalog() {
85
+ return [
86
+ 'sw-list-row-unread',
87
+ 'sw-list-row-tag',
88
+ 'sw-list-item-action',
89
+ 'sw-list-row-action',
90
+ ];
91
+ }
@@ -1,5 +1,6 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
2
  import { cssListRowClasses, listRowClassCatalog } from './css-list-row.js';
3
+ import { cssListRowTrailingClasses, listRowTrailingClassCatalog, } from './css-list-row-trailing.js';
3
4
  import { blockEdgeShadeRules, scrollRegionRules } from './css-scroll-region.js';
4
5
  const label = typographyVariants.label;
5
6
  const caption = typographyVariants.caption;
@@ -69,7 +70,9 @@ export function cssListClasses() {
69
70
 
70
71
  ${boundedRules()}
71
72
 
72
- ${cssListRowClasses()}`;
73
+ ${cssListRowClasses()}
74
+
75
+ ${cssListRowTrailingClasses()}`;
73
76
  }
74
77
  export function listClassCatalog() {
75
78
  return [
@@ -81,5 +84,6 @@ export function listClassCatalog() {
81
84
  'sw-list-group-scroll',
82
85
  'sw-list-group-footer',
83
86
  ...listRowClassCatalog(),
87
+ ...listRowTrailingClassCatalog(),
84
88
  ];
85
89
  }
@@ -0,0 +1,2 @@
1
+ export declare function cssMarkdownClasses(): string;
2
+ export declare function markdownClassCatalog(): string[];
@@ -0,0 +1,144 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ const { body, title, label, caption } = typographyVariants;
3
+ /*
4
+ * A page of Markdown in body type: blocks a space-4 apart, a heading a
5
+ * space-6 below what comes before it and a space-2 above what follows.
6
+ * `#` is the title size, `##` body size and `###` label size, all at the
7
+ * title's weight. At zero specificity (`:where`), so a consumer's layout
8
+ * can still space the view itself.
9
+ */
10
+ function flowRules() {
11
+ return `.sw-markdown {
12
+ color: var(--sw-color-text);
13
+ font-family: var(--sw-font-sans);
14
+ font-size: ${body.fontSize}px;
15
+ letter-spacing: ${body.letterSpacing}px;
16
+ line-height: ${body.lineHeight}px;
17
+ overflow-wrap: anywhere;
18
+ }
19
+
20
+ :where(.sw-markdown) > *,
21
+ :where(.sw-markdown-quote) > *,
22
+ :where(.sw-markdown-list li) > * { margin-block: 0; }
23
+
24
+ :where(.sw-markdown, .sw-markdown-quote, .sw-markdown-list li) > * + * {
25
+ margin-block-start: var(--sw-space-4);
26
+ }
27
+
28
+ :where(.sw-markdown) > * + .sw-markdown-heading { margin-block-start: var(--sw-space-6); }
29
+ :where(.sw-markdown) > .sw-markdown-heading + * { margin-block-start: var(--sw-space-2); }
30
+
31
+ .sw-markdown-heading {
32
+ color: var(--sw-color-text);
33
+ font-weight: ${title.fontWeight};
34
+ scroll-margin-block-start: var(--sw-space-6);
35
+ }
36
+
37
+ .sw-markdown-heading-1 {
38
+ font-size: ${title.fontSize}px;
39
+ letter-spacing: ${title.letterSpacing}px;
40
+ line-height: ${title.lineHeight}px;
41
+ }
42
+
43
+ .sw-markdown-heading-2 {
44
+ font-size: ${body.fontSize}px;
45
+ line-height: ${body.lineHeight}px;
46
+ }
47
+
48
+ .sw-markdown-heading-3 {
49
+ font-size: ${label.fontSize}px;
50
+ letter-spacing: ${label.letterSpacing}px;
51
+ line-height: ${label.lineHeight}px;
52
+ }`;
53
+ }
54
+ /*
55
+ * Lists hang their markers in the page's margin a space-5 in; a tight
56
+ * list's items are a space-2 apart. A link is the accent, underlined, so
57
+ * it never depends on colour alone, and thickens to the focus ring's
58
+ * width while hovered. In the highlighted box, whose tint neither the
59
+ * accent nor the muted colour clears at 4.5:1 in every palette, links
60
+ * (still underlined) and photo captions are the text colour.
61
+ */
62
+ function inlineRules() {
63
+ return `.sw-markdown-list {
64
+ padding-inline-start: var(--sw-space-5);
65
+ }
66
+
67
+ .sw-markdown-list > li + li { margin-block-start: var(--sw-space-2); }
68
+
69
+ [data-theme] .sw-markdown-link {
70
+ color: var(--sw-color-accent);
71
+ text-decoration-line: underline;
72
+ }
73
+
74
+ [data-theme] .sw-markdown-link:hover {
75
+ text-decoration-thickness: var(--sw-focus-ring-width);
76
+ }
77
+
78
+ [data-theme] .sw-markdown-quote :is(.sw-markdown-link, .sw-markdown-caption) {
79
+ color: var(--sw-color-text);
80
+ }`;
81
+ }
82
+ /*
83
+ * A block quote is the highlighted box: the accent's subtle tint with an
84
+ * accent bar at its start, at the small radius. A photo fills the page's
85
+ * width at the medium radius, its caption muted under it.
86
+ */
87
+ function boxRules() {
88
+ return `.sw-markdown-quote {
89
+ background: var(--sw-color-accentSubtle);
90
+ border-inline-start: var(--sw-space-1) solid var(--sw-color-accent);
91
+ border-radius: var(--sw-radius-sm);
92
+ margin-inline: 0;
93
+ padding: var(--sw-space-3) var(--sw-space-4);
94
+ }
95
+
96
+ .sw-markdown-figure {
97
+ display: flex;
98
+ flex-direction: column;
99
+ gap: var(--sw-space-2);
100
+ margin-inline: 0;
101
+ }
102
+
103
+ .sw-markdown-image {
104
+ border-radius: var(--sw-radius-md);
105
+ display: block;
106
+ height: auto;
107
+ width: 100%;
108
+ }
109
+
110
+ .sw-markdown-caption {
111
+ color: var(--sw-color-muted);
112
+ font-size: ${caption.fontSize}px;
113
+ letter-spacing: ${caption.letterSpacing}px;
114
+ line-height: ${caption.lineHeight}px;
115
+ }
116
+
117
+ @media (forced-colors: active) {
118
+ .sw-markdown-quote { border: 1px solid CanvasText; border-inline-start-width: var(--sw-space-1); }
119
+ }`;
120
+ }
121
+ export function cssMarkdownClasses() {
122
+ return `${flowRules()}
123
+
124
+ ${inlineRules()}
125
+
126
+ ${boxRules()}`;
127
+ }
128
+ export function markdownClassCatalog() {
129
+ return [
130
+ 'sw-markdown',
131
+ 'sw-markdown-paragraph',
132
+ 'sw-markdown-heading',
133
+ 'sw-markdown-heading-1',
134
+ 'sw-markdown-heading-2',
135
+ 'sw-markdown-heading-3',
136
+ 'sw-markdown-list',
137
+ 'sw-markdown-item-text',
138
+ 'sw-markdown-link',
139
+ 'sw-markdown-quote',
140
+ 'sw-markdown-figure',
141
+ 'sw-markdown-image',
142
+ 'sw-markdown-caption',
143
+ ];
144
+ }
@@ -47,6 +47,7 @@ ${control}
47
47
  --sw-dialog-max: 32rem;
48
48
  --sw-dialog-max-lg: 56rem;
49
49
  --sw-dialog-max-xl: 65rem;
50
+ --sw-reading-max: 46rem;
50
51
  --sw-select-max: 16rem;
51
52
  --sw-split-min: ${splitScale.min}rem;
52
53
  --sw-split-size: ${splitScale.size}rem;
@@ -7,7 +7,11 @@ import { switchClassCatalog } from './css-switch.js';
7
7
  import { dateFieldClassCatalog } from './css-date-field.js';
8
8
  import { calendarButtonClassCatalog } from './css-calendar-button.js';
9
9
  import { dateRangeClassCatalog } from './css-date-range.js';
10
+ import { fileFieldClassCatalog } from './css-file-field.js';
11
+ import { articleClassCatalog } from './css-article.js';
12
+ import { documentViewerClassCatalog } from './css-document-viewer.js';
10
13
  import { listClassCatalog } from './css-list.js';
14
+ import { markdownClassCatalog } from './css-markdown.js';
11
15
  import { countPillClassCatalog } from './css-count-pill.js';
12
16
  import { checkboxClassCatalog } from './css-checkbox.js';
13
17
  import { radioGroupClassCatalog } from './css-radio-group.js';
@@ -104,6 +108,10 @@ export function utilityClassCatalog() {
104
108
  ...calendarButtonClassCatalog(),
105
109
  ...dateRangeClassCatalog(),
106
110
  ...listClassCatalog(),
111
+ ...fileFieldClassCatalog(),
112
+ ...articleClassCatalog(),
113
+ ...documentViewerClassCatalog(),
114
+ ...markdownClassCatalog(),
107
115
  ...countPillClassCatalog(),
108
116
  ...checkboxClassCatalog(),
109
117
  ...radioGroupClassCatalog(),
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The PDF.js worker for `DocumentViewer`, at the exact PDF.js version
3
+ * Scalewing pins and from the same legacy build: a module worker entry
4
+ * that starts PDF.js's worker on import. A consumer bundles it as a worker
5
+ * and passes its URL as `workerSrc`, such as Vite's
6
+ * `import workerSrc from '@scalewing/react/document-viewer-worker?worker&url'`,
7
+ * so the viewer and its worker are always the same version.
8
+ */
9
+ import 'pdfjs-dist/legacy/build/pdf.worker.min.mjs';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The PDF.js worker for `DocumentViewer`, at the exact PDF.js version
3
+ * Scalewing pins and from the same legacy build: a module worker entry
4
+ * that starts PDF.js's worker on import. A consumer bundles it as a worker
5
+ * and passes its URL as `workerSrc`, such as Vite's
6
+ * `import workerSrc from '@scalewing/react/document-viewer-worker?worker&url'`,
7
+ * so the viewer and its worker are always the same version.
8
+ */
9
+ import 'pdfjs-dist/legacy/build/pdf.worker.min.mjs';