@react-x11/components 0.11.0 → 0.13.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 (126) hide show
  1. package/dist/flow/node.d.ts +14 -6
  2. package/dist/flow/node.d.ts.map +1 -1
  3. package/dist/flow/node.js +82 -64
  4. package/dist/flow/node.js.map +1 -1
  5. package/dist/html/controls.d.ts +38 -10
  6. package/dist/html/controls.d.ts.map +1 -1
  7. package/dist/html/controls.js +43 -58
  8. package/dist/html/controls.js.map +1 -1
  9. package/dist/html/css/cascade.d.ts +134 -20
  10. package/dist/html/css/cascade.d.ts.map +1 -1
  11. package/dist/html/css/cascade.js +538 -128
  12. package/dist/html/css/cascade.js.map +1 -1
  13. package/dist/html/css/color.d.ts +19 -0
  14. package/dist/html/css/color.d.ts.map +1 -1
  15. package/dist/html/css/color.js +86 -0
  16. package/dist/html/css/color.js.map +1 -1
  17. package/dist/html/css/parse.d.ts +40 -2
  18. package/dist/html/css/parse.d.ts.map +1 -1
  19. package/dist/html/css/parse.js +391 -13
  20. package/dist/html/css/parse.js.map +1 -1
  21. package/dist/html/css/style.d.ts +52 -1
  22. package/dist/html/css/style.d.ts.map +1 -1
  23. package/dist/html/css/style.js +328 -11
  24. package/dist/html/css/style.js.map +1 -1
  25. package/dist/html/css/ua.d.ts +11 -1
  26. package/dist/html/css/ua.d.ts.map +1 -1
  27. package/dist/html/css/ua.js +73 -7
  28. package/dist/html/css/ua.js.map +1 -1
  29. package/dist/html/css/values.d.ts +10 -1
  30. package/dist/html/css/values.d.ts.map +1 -1
  31. package/dist/html/css/values.js +27 -2
  32. package/dist/html/css/values.js.map +1 -1
  33. package/dist/html/css/vars.d.ts +5 -0
  34. package/dist/html/css/vars.d.ts.map +1 -1
  35. package/dist/html/css/vars.js +2 -2
  36. package/dist/html/css/vars.js.map +1 -1
  37. package/dist/html/dom.d.ts.map +1 -1
  38. package/dist/html/dom.js +38 -3
  39. package/dist/html/dom.js.map +1 -1
  40. package/dist/html/fonts.d.ts +25 -1
  41. package/dist/html/fonts.d.ts.map +1 -1
  42. package/dist/html/fonts.js +99 -6
  43. package/dist/html/fonts.js.map +1 -1
  44. package/dist/html/form.d.ts +200 -0
  45. package/dist/html/form.d.ts.map +1 -0
  46. package/dist/html/form.js +801 -0
  47. package/dist/html/form.js.map +1 -0
  48. package/dist/html/index.d.ts +12 -0
  49. package/dist/html/index.d.ts.map +1 -1
  50. package/dist/html/index.js +30 -264
  51. package/dist/html/index.js.map +1 -1
  52. package/dist/html/layout/block.d.ts +11 -2
  53. package/dist/html/layout/block.d.ts.map +1 -1
  54. package/dist/html/layout/block.js +186 -19
  55. package/dist/html/layout/block.js.map +1 -1
  56. package/dist/html/layout/boxes.d.ts +65 -7
  57. package/dist/html/layout/boxes.d.ts.map +1 -1
  58. package/dist/html/layout/boxes.js +87 -22
  59. package/dist/html/layout/boxes.js.map +1 -1
  60. package/dist/html/layout/cache.d.ts +2 -2
  61. package/dist/html/layout/cache.d.ts.map +1 -1
  62. package/dist/html/layout/cache.js +8 -3
  63. package/dist/html/layout/cache.js.map +1 -1
  64. package/dist/html/layout/flex.js +4 -1
  65. package/dist/html/layout/flex.js.map +1 -1
  66. package/dist/html/layout/floats.d.ts +4 -0
  67. package/dist/html/layout/floats.d.ts.map +1 -1
  68. package/dist/html/layout/floats.js +26 -0
  69. package/dist/html/layout/floats.js.map +1 -1
  70. package/dist/html/layout/inline.d.ts +26 -2
  71. package/dist/html/layout/inline.d.ts.map +1 -1
  72. package/dist/html/layout/inline.js +1232 -115
  73. package/dist/html/layout/inline.js.map +1 -1
  74. package/dist/html/node.d.ts +120 -15
  75. package/dist/html/node.d.ts.map +1 -1
  76. package/dist/html/node.js +993 -174
  77. package/dist/html/node.js.map +1 -1
  78. package/dist/html/paint.d.ts +75 -2
  79. package/dist/html/paint.d.ts.map +1 -1
  80. package/dist/html/paint.js +836 -200
  81. package/dist/html/paint.js.map +1 -1
  82. package/dist/html/surfaces.d.ts +7 -2
  83. package/dist/html/surfaces.d.ts.map +1 -1
  84. package/dist/html/surfaces.js +16 -0
  85. package/dist/html/surfaces.js.map +1 -1
  86. package/dist/html/widgets.d.ts +29 -0
  87. package/dist/html/widgets.d.ts.map +1 -0
  88. package/dist/html/widgets.js +620 -0
  89. package/dist/html/widgets.js.map +1 -0
  90. package/dist/index.d.ts +1 -1
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js.map +1 -1
  93. package/dist/internal/window.d.ts +22 -0
  94. package/dist/internal/window.d.ts.map +1 -1
  95. package/dist/internal/window.js +35 -8
  96. package/dist/internal/window.js.map +1 -1
  97. package/dist/richtext/node.d.ts +7 -0
  98. package/dist/richtext/node.d.ts.map +1 -1
  99. package/dist/richtext/node.js.map +1 -1
  100. package/package.json +6 -4
  101. package/src/flow/node.ts +80 -64
  102. package/src/html/controls.ts +79 -58
  103. package/src/html/css/cascade.ts +610 -146
  104. package/src/html/css/color.ts +87 -0
  105. package/src/html/css/parse.ts +431 -13
  106. package/src/html/css/style.ts +367 -11
  107. package/src/html/css/ua.ts +76 -8
  108. package/src/html/css/values.ts +31 -3
  109. package/src/html/css/vars.ts +2 -2
  110. package/src/html/dom.ts +41 -3
  111. package/src/html/fonts.ts +116 -6
  112. package/src/html/form.ts +962 -0
  113. package/src/html/index.ts +49 -300
  114. package/src/html/layout/block.ts +231 -16
  115. package/src/html/layout/boxes.ts +133 -32
  116. package/src/html/layout/cache.ts +8 -3
  117. package/src/html/layout/flex.ts +4 -1
  118. package/src/html/layout/floats.ts +24 -0
  119. package/src/html/layout/inline.ts +1445 -108
  120. package/src/html/node.ts +1010 -166
  121. package/src/html/paint.ts +980 -270
  122. package/src/html/surfaces.ts +21 -1
  123. package/src/html/widgets.ts +735 -0
  124. package/src/index.ts +1 -0
  125. package/src/internal/window.ts +36 -8
  126. package/src/richtext/node.ts +7 -0
@@ -22,9 +22,15 @@
22
22
  import type { Element } from 'domhandler';
23
23
 
24
24
  import { attr, tagOf } from './dom.js';
25
+ import {
26
+ optionElements,
27
+ optionLabel,
28
+ optionValue,
29
+ selectedOptions,
30
+ } from './form.js';
25
31
  import type { ComputedStyle } from './css/style.js';
26
32
  import { isTransparent } from './css/values.js';
27
- import type { BoxTree, ReplacedKind } from './layout/boxes.js';
33
+ import type { Box, BoxTree, ReplacedKind } from './layout/boxes.js';
28
34
  import type { FontsLike } from './layout/inline.js';
29
35
 
30
36
  /** The palette numbers a control's box has to reserve room for. */
@@ -48,23 +54,48 @@ export interface ControlRect {
48
54
  width: number;
49
55
  height: number;
50
56
  /**
51
- * Set on a text field whose own box the author styled — gave it a border
52
- * or a background (`styledField`). The document draws that box, and the
53
- * widget goes bare inside its content box, here, with no frame or fill
54
- * of its own and its text in the element's colour and font.
57
+ * The element's computed face and size: what `measureControl` measured
58
+ * its box in, and so what the widget sets its text in. The palette's,
59
+ * from the UA sheet, unless the page set its own — `font: inherit`, which
60
+ * a CSS reset gives every control, for one.
61
+ */
62
+ fontFamily: string;
63
+ fontSize: number;
64
+ /**
65
+ * Set on a text field or a `<select>` whose own box the author styled —
66
+ * gave it a border or a background (`styledField`). The document draws
67
+ * that box, and the widget goes bare inside its content box, here, with
68
+ * no frame or fill of its own and its text in the element's colour and
69
+ * font.
55
70
  */
56
71
  bare?: BareField;
72
+ /**
73
+ * How opaque the widget is drawn, where it is less than 1: the element's
74
+ * own `opacity` times every ancestor's, since each fades what is in it as
75
+ * a group. At 0 the widget is not seen and still takes a press, as the
76
+ * element does in a browser — the checkbox a CSS-only dropdown lays,
77
+ * invisible, over its label.
78
+ */
79
+ opacity?: number;
57
80
  }
58
81
 
59
- /** Where a styled text field's widget goes, and how its text looks. */
82
+ /** Where a styled field's widget goes, and how its text looks. */
60
83
  export interface BareField {
61
84
  x: number;
62
85
  y: number;
63
86
  width: number;
64
87
  height: number;
65
88
  color: string;
89
+ /** @deprecated The rect's own `fontFamily`, which every rect carries. */
66
90
  fontFamily: string;
91
+ /** @deprecated The rect's own `fontSize`, which every rect carries. */
67
92
  fontSize: number;
93
+ /**
94
+ * A `<select>`'s: whether it draws its arrow. It does unless the page set
95
+ * `appearance: none`, which is how a page that draws its own arrow — as a
96
+ * background image, most often — says so.
97
+ */
98
+ chevron?: boolean;
68
99
  }
69
100
 
70
101
  /** The rectangles every control in a laid-out document landed on. */
@@ -73,6 +104,8 @@ export function controlRectsOf(tree: BoxTree): ControlRect[] {
73
104
  for (const box of tree.controls) {
74
105
  if (!box.el) continue;
75
106
  if (box.width <= 0 || box.height <= 0) continue;
107
+ // a hidden element draws nothing and takes no press (CSS 2.1 11.2)
108
+ if (box.style.visibility !== 'visible') continue;
76
109
  const rect: ControlRect = {
77
110
  element: box.el,
78
111
  kind: box.replaced,
@@ -80,7 +113,14 @@ export function controlRectsOf(tree: BoxTree): ControlRect[] {
80
113
  y: box.y,
81
114
  width: box.width,
82
115
  height: box.height,
116
+ fontFamily: box.style.fontFamily,
117
+ fontSize: box.style.fontSize,
83
118
  };
119
+ let opacity = 1;
120
+ for (let at: Box | null = box; at; at = at.parent) {
121
+ opacity *= at.style.opacity;
122
+ }
123
+ if (opacity < 1) rect.opacity = Math.max(0, opacity);
84
124
  if (styledField(box.replaced, box.style)) {
85
125
  rect.bare = {
86
126
  x: box.contentX,
@@ -90,6 +130,9 @@ export function controlRectsOf(tree: BoxTree): ControlRect[] {
90
130
  color: box.style.color,
91
131
  fontFamily: box.style.fontFamily,
92
132
  fontSize: box.style.fontSize,
133
+ ...(box.replaced === 'select' && {
134
+ chevron: box.style.appearance !== 'none',
135
+ }),
93
136
  };
94
137
  }
95
138
  out.push(rect);
@@ -98,17 +141,23 @@ export function controlRectsOf(tree: BoxTree): ControlRect[] {
98
141
  }
99
142
 
100
143
  /**
101
- * Whether a text field's box is the author's to draw: one given a border or
102
- * a background of its own, which the UA sheet gives no control, or set to
144
+ * Whether a field's box is the author's to draw: one given a border or a
145
+ * background of its own, which the UA sheet gives no control, or set to
103
146
  * `appearance: none`, which says so outright. A browser drops a field's
104
147
  * native look for the author's then (CSS UI 4 7.1, `appearance`), and so
105
148
  * does this: the widget's frame and fill would hide the author's, and what
106
149
  * they would draw is the theme's rather than the page's. `appearance: none`
107
150
  * is how a design system writes every field it has — meetup.com's search
108
151
  * pill holds two with no background, only one of them with a border.
152
+ *
153
+ * A `<select>` is one too. A browser keeps its arrow when the page gave it a
154
+ * border or a background (Blink's `menulist-button`), and leaves that out
155
+ * as well at `appearance: none` — `BareField.chevron`.
109
156
  */
110
157
  export function styledField(kind: ReplacedKind, style: ComputedStyle): boolean {
111
- if (kind !== 'input' && kind !== 'textarea') return false;
158
+ if (kind !== 'input' && kind !== 'textarea' && kind !== 'select') {
159
+ return false;
160
+ }
112
161
  return (
113
162
  style.appearance === 'none' ||
114
163
  !isTransparent(style.backgroundColor) ||
@@ -168,6 +217,16 @@ export function measureControl(
168
217
  }
169
218
  case 'select': {
170
219
  const widest = optionWidths(el);
220
+ // a select the page styled is its widest option and its arrow, and
221
+ // the room around them is the page's
222
+ if (styledField(kind, style)) {
223
+ return {
224
+ width: Math.round(
225
+ ch * widest + (style.appearance === 'none' ? 0 : em),
226
+ ),
227
+ height: lineHeight,
228
+ };
229
+ }
171
230
  // The room for the chevron is the widget's, not the document's, but
172
231
  // the document has to reserve it or the last letter of the widest
173
232
  // option sits under it.
@@ -220,9 +279,11 @@ export function buttonLabel(el: Element): string {
220
279
  const trimmed = text.trim();
221
280
  if (trimmed) return trimmed;
222
281
  }
282
+ const type = (attr(el, 'type') ?? '').trim().toLowerCase();
283
+ // an image button with no image yet says what the image would have
284
+ if (type === 'image') return attr(el, 'alt') || attr(el, 'value') || 'Submit';
223
285
  const value = attr(el, 'value');
224
286
  if (value) return value;
225
- const type = (attr(el, 'type') ?? '').toLowerCase();
226
287
  if (type === 'submit') return 'Submit';
227
288
  if (type === 'reset') return 'Reset';
228
289
  return attr(el, 'alt') ?? 'Button';
@@ -230,57 +291,17 @@ export function buttonLabel(el: Element): string {
230
291
 
231
292
  /** A `<select>`'s options, as the widget's item list. */
232
293
  export function optionsOf(el: Element): { value: string; label: string }[] {
233
- const out: { value: string; label: string }[] = [];
234
- const walk = (node: Element): void => {
235
- for (const child of node.children) {
236
- if (child.type !== 'tag') continue;
237
- const tag = tagOf(child);
238
- if (tag === 'option') {
239
- let label = '';
240
- for (const kid of child.children) {
241
- if (kid.type === 'text') label += kid.data;
242
- }
243
- const trimmed = label.trim();
244
- out.push({ value: attr(child, 'value') ?? trimmed, label: trimmed });
245
- } else if (tag === 'optgroup') {
246
- walk(child);
247
- }
248
- }
249
- };
250
- walk(el);
251
- return out;
294
+ return optionElements(el).map((option) => ({
295
+ value: optionValue(option),
296
+ label: optionLabel(option),
297
+ }));
252
298
  }
253
299
 
254
- /** Which option a `<select>` starts on: `selected`, else the first. */
300
+ /** Which option a `<select>` shows: the one it has selected, which is the
301
+ * first where none is marked (`selectedOptions`). */
255
302
  export function selectedOption(el: Element): string | null {
256
- const options = optionsOf(el);
257
- const walk = (node: Element): string | null => {
258
- for (const child of node.children) {
259
- if (child.type !== 'tag') continue;
260
- if (tagOf(child) === 'option' && attr(child, 'selected') !== undefined) {
261
- let label = '';
262
- for (const kid of child.children) {
263
- if (kid.type === 'text') label += kid.data;
264
- }
265
- return attr(child, 'value') ?? label.trim();
266
- }
267
- const nested = walk(child);
268
- if (nested !== null) return nested;
269
- }
270
- return null;
271
- };
272
- return walk(el) ?? options[0]?.value ?? null;
273
- }
274
-
275
- /** A `<textarea>`'s initial text — its content, not an attribute. */
276
- export function textareaValue(el: Element): string {
277
- let text = '';
278
- for (const child of el.children) {
279
- if (child.type === 'text') text += child.data;
280
- }
281
- // HTML drops one leading newline after the open tag, which is why a
282
- // pretty-printed `<textarea>` does not start with a blank line.
283
- return text.replace(/^\r?\n/, '');
303
+ const [option] = selectedOptions(el);
304
+ return option ? optionValue(option) : null;
284
305
  }
285
306
 
286
307
  function numberAttr(el: Element, name: string): number | null {