@react-x11/components 0.13.0 → 0.14.1
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.
- package/dist/html/controls.d.ts +29 -2
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +70 -2
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts +42 -1
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +336 -16
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +18 -0
- package/dist/html/css/color.d.ts.map +1 -1
- package/dist/html/css/color.js +27 -0
- package/dist/html/css/color.js.map +1 -1
- package/dist/html/css/parse.d.ts +18 -4
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +66 -28
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/shapes.d.ts +44 -0
- package/dist/html/css/shapes.d.ts.map +1 -0
- package/dist/html/css/shapes.js +194 -0
- package/dist/html/css/shapes.js.map +1 -0
- package/dist/html/css/style.d.ts +128 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +547 -144
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/transform.d.ts +85 -0
- package/dist/html/css/transform.d.ts.map +1 -0
- package/dist/html/css/transform.js +425 -0
- package/dist/html/css/transform.js.map +1 -0
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +14 -2
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +4 -0
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/fonts.d.ts +55 -5
- package/dist/html/fonts.d.ts.map +1 -1
- package/dist/html/fonts.js +284 -23
- package/dist/html/fonts.js.map +1 -1
- package/dist/html/layout/axes.d.ts +14 -0
- package/dist/html/layout/axes.d.ts.map +1 -0
- package/dist/html/layout/axes.js +28 -0
- package/dist/html/layout/axes.js.map +1 -0
- package/dist/html/layout/block.d.ts +104 -12
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +410 -39
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +72 -1
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +40 -2
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +1 -1
- package/dist/html/layout/cache.d.ts.map +1 -1
- package/dist/html/layout/cache.js +6 -0
- package/dist/html/layout/cache.js.map +1 -1
- package/dist/html/layout/css-grid.d.ts.map +1 -1
- package/dist/html/layout/css-grid.js +144 -24
- package/dist/html/layout/css-grid.js.map +1 -1
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +585 -85
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/inline.d.ts +3 -0
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +144 -54
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/multicol.d.ts +42 -0
- package/dist/html/layout/multicol.d.ts.map +1 -0
- package/dist/html/layout/multicol.js +897 -0
- package/dist/html/layout/multicol.js.map +1 -0
- package/dist/html/layout/shaping.d.ts +7 -0
- package/dist/html/layout/shaping.d.ts.map +1 -1
- package/dist/html/layout/shaping.js +25 -20
- package/dist/html/layout/shaping.js.map +1 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +97 -31
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +4 -0
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +319 -47
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +83 -5
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +1474 -217
- package/dist/html/paint.js.map +1 -1
- package/dist/html/svg.d.ts +39 -9
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +482 -37
- package/dist/html/svg.js.map +1 -1
- package/dist/html/widgets.js +87 -7
- package/dist/html/widgets.js.map +1 -1
- package/dist/html/woff2.d.ts +14 -0
- package/dist/html/woff2.d.ts.map +1 -0
- package/dist/html/woff2.js +608 -0
- package/dist/html/woff2.js.map +1 -0
- package/dist/maps/controller.d.ts +17 -1
- package/dist/maps/controller.d.ts.map +1 -1
- package/dist/maps/controller.js +27 -7
- package/dist/maps/controller.js.map +1 -1
- package/dist/richtext/node.d.ts +8 -1
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +5 -1
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +25 -6
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +240 -24
- package/dist/richtext/runs.js.map +1 -1
- package/package.json +1 -1
- package/src/html/controls.ts +85 -2
- package/src/html/css/cascade.ts +366 -8
- package/src/html/css/color.ts +29 -0
- package/src/html/css/parse.ts +71 -31
- package/src/html/css/shapes.ts +239 -0
- package/src/html/css/style.ts +652 -147
- package/src/html/css/transform.ts +459 -0
- package/src/html/css/ua.ts +15 -2
- package/src/html/css/values.ts +4 -0
- package/src/html/fonts.ts +312 -29
- package/src/html/layout/axes.ts +62 -0
- package/src/html/layout/block.ts +466 -37
- package/src/html/layout/boxes.ts +95 -3
- package/src/html/layout/cache.ts +6 -0
- package/src/html/layout/css-grid.ts +169 -32
- package/src/html/layout/flex.ts +686 -79
- package/src/html/layout/inline.ts +149 -55
- package/src/html/layout/multicol.ts +977 -0
- package/src/html/layout/shaping.ts +27 -18
- package/src/html/layout/table.ts +105 -35
- package/src/html/node.ts +354 -48
- package/src/html/paint.ts +1804 -315
- package/src/html/svg.ts +573 -36
- package/src/html/widgets.ts +95 -9
- package/src/html/woff2.ts +612 -0
- package/src/maps/controller.ts +28 -7
- package/src/richtext/node.ts +13 -2
- package/src/richtext/runs.ts +313 -35
package/src/html/widgets.ts
CHANGED
|
@@ -25,11 +25,11 @@ import type {} from 'react-x11/jsx-runtime';
|
|
|
25
25
|
|
|
26
26
|
import { cancelLater, later } from '../internal/timers.js';
|
|
27
27
|
import { hx } from './hx.js';
|
|
28
|
-
import { attr, tagOf } from './dom.js';
|
|
28
|
+
import { attr, isElement, tagOf } from './dom.js';
|
|
29
29
|
import type { Document, Element } from './dom.js';
|
|
30
30
|
import type { HtmlViewNode } from './node.js';
|
|
31
31
|
import type { RootLook } from './css/style.js';
|
|
32
|
-
import { buttonLabel, optionsOf, selectedOption } from './controls.js';
|
|
32
|
+
import { between, buttonLabel, optionsOf, selectedOption } from './controls.js';
|
|
33
33
|
import type { BareField, ControlRect } from './controls.js';
|
|
34
34
|
import {
|
|
35
35
|
FormState,
|
|
@@ -433,12 +433,28 @@ function renderControl(
|
|
|
433
433
|
const readOnly = attr(el, 'readonly') !== undefined;
|
|
434
434
|
// a field whose box the document draws takes its content box
|
|
435
435
|
const at = rect.bare ?? rect;
|
|
436
|
+
const left = Math.round(at.x);
|
|
437
|
+
const top = Math.round(at.y);
|
|
438
|
+
const width = Math.round(at.width);
|
|
439
|
+
const height = Math.round(at.height);
|
|
440
|
+
// What the widget shows through: its own rectangle, or where the
|
|
441
|
+
// document cuts the element, what the cut leaves of it — out to where a
|
|
442
|
+
// focus ring reaches, which a clip around the element cuts as it cuts
|
|
443
|
+
// the element, and one that leaves the box whole does not.
|
|
444
|
+
const port = rect.clip
|
|
445
|
+
? between(rect.clip, {
|
|
446
|
+
x: left - RING_REACH,
|
|
447
|
+
y: top - RING_REACH,
|
|
448
|
+
width: width + 2 * RING_REACH,
|
|
449
|
+
height: height + 2 * RING_REACH,
|
|
450
|
+
})
|
|
451
|
+
: { x: left, y: top, width, height };
|
|
436
452
|
const frame: Style = {
|
|
437
453
|
position: 'absolute',
|
|
438
|
-
left:
|
|
439
|
-
top:
|
|
440
|
-
width
|
|
441
|
-
height
|
|
454
|
+
left: left - port.x,
|
|
455
|
+
top: top - port.y,
|
|
456
|
+
width,
|
|
457
|
+
height,
|
|
442
458
|
// The face and the size the box was measured in, the element's: the
|
|
443
459
|
// palette's from the UA sheet, or the page's where it set its own. A
|
|
444
460
|
// field inherits them, and so does a `<Button>`'s or a `<Select>`'s
|
|
@@ -452,6 +468,7 @@ function renderControl(
|
|
|
452
468
|
...(rect.opacity !== undefined && { opacity: rect.opacity }),
|
|
453
469
|
};
|
|
454
470
|
const field = rect.bare ? bareField(rect.bare) : fieldChrome(look);
|
|
471
|
+
const order = tabOrder(el);
|
|
455
472
|
// A button or a select whose font the page set is the drawn control: a
|
|
456
473
|
// native bezel sets its title at AppKit's size, whatever it is handed,
|
|
457
474
|
// in a bezel only as tall as that title, where the box was measured for
|
|
@@ -474,6 +491,7 @@ function renderControl(
|
|
|
474
491
|
widget = h(Checkbox, {
|
|
475
492
|
checked: attr(el, 'checked') !== undefined,
|
|
476
493
|
disabled,
|
|
494
|
+
...order,
|
|
477
495
|
// the element's whole box takes the press, as it does in a browser:
|
|
478
496
|
// a page that sizes one over its label, invisible, means the label
|
|
479
497
|
style: { width: '100%', height: '100%' },
|
|
@@ -482,6 +500,9 @@ function renderControl(
|
|
|
482
500
|
break;
|
|
483
501
|
case 'radio': {
|
|
484
502
|
const value = attr(el, 'value') ?? 'on';
|
|
503
|
+
// No `tabindex` reaches a radio: core's `<Radio>` takes no props for
|
|
504
|
+
// the node it draws, as the other widgets do, and the group's box is
|
|
505
|
+
// not the one that takes the focus.
|
|
485
506
|
widget = h(
|
|
486
507
|
RadioGroup,
|
|
487
508
|
{
|
|
@@ -496,6 +517,7 @@ function renderControl(
|
|
|
496
517
|
widget = h(Button, {
|
|
497
518
|
label: buttonLabel(el),
|
|
498
519
|
disabled,
|
|
520
|
+
...order,
|
|
499
521
|
...(ownFont && { native: false }),
|
|
500
522
|
style: { width: '100%', height: '100%' },
|
|
501
523
|
onPress: () => ctx.press(el),
|
|
@@ -506,7 +528,11 @@ function renderControl(
|
|
|
506
528
|
widget = h(Select, {
|
|
507
529
|
options: options.map((o) => ({ value: o.value, label: o.label })),
|
|
508
530
|
value: selectedOption(el) ?? undefined,
|
|
531
|
+
// a `<select>` with nothing selected has no options, and shows
|
|
532
|
+
// none: core's "Select…" is an application's prompt, not a page's
|
|
533
|
+
placeholder: '',
|
|
509
534
|
disabled,
|
|
535
|
+
...order,
|
|
510
536
|
style: rect.bare
|
|
511
537
|
? [BARE_TRIGGER, { width: '100%', height: '100%' }]
|
|
512
538
|
: { width: '100%', height: '100%' },
|
|
@@ -547,6 +573,7 @@ function renderControl(
|
|
|
547
573
|
widget = hx('textarea', {
|
|
548
574
|
defaultValue: forms.value(el),
|
|
549
575
|
maxLength: maxLength(el),
|
|
576
|
+
...order,
|
|
550
577
|
style: [field, { width: '100%', height: '100%' }],
|
|
551
578
|
onChange: readOnly ? undefined : (ev) => typed(ev.value),
|
|
552
579
|
});
|
|
@@ -557,6 +584,7 @@ function renderControl(
|
|
|
557
584
|
defaultValue: forms.value(el),
|
|
558
585
|
placeholder: attr(el, 'placeholder'),
|
|
559
586
|
maxLength: maxLength(el),
|
|
587
|
+
...order,
|
|
560
588
|
// Core's word for a password field: nothing in it reaches a
|
|
561
589
|
// selection, PRIMARY included.
|
|
562
590
|
sensitive: type === 'password',
|
|
@@ -578,18 +606,76 @@ function renderControl(
|
|
|
578
606
|
default:
|
|
579
607
|
return null;
|
|
580
608
|
}
|
|
609
|
+
// Two boxes, always: the one the widget shows through, and in it the
|
|
610
|
+
// element's. A widget the document comes to cut, or stops cutting, is
|
|
611
|
+
// the same widget in the same place in the tree, and keeps its focus
|
|
612
|
+
// and its caret.
|
|
581
613
|
return hx(
|
|
582
614
|
'box',
|
|
583
615
|
{
|
|
584
616
|
key,
|
|
585
|
-
ref: ctx.widgets.refOf(el),
|
|
586
|
-
style: frame,
|
|
587
617
|
selectable: false,
|
|
618
|
+
style: {
|
|
619
|
+
position: 'absolute',
|
|
620
|
+
left: port.x,
|
|
621
|
+
top: port.y,
|
|
622
|
+
width: port.width,
|
|
623
|
+
height: port.height,
|
|
624
|
+
// cut to it, and nothing itself: a press beside the widget is the
|
|
625
|
+
// document's
|
|
626
|
+
...(rect.clip && { overflow: 'hidden', pointerEvents: 'box-none' }),
|
|
627
|
+
},
|
|
588
628
|
},
|
|
589
|
-
|
|
629
|
+
hx(
|
|
630
|
+
'box',
|
|
631
|
+
{
|
|
632
|
+
ref: ctx.widgets.refOf(el),
|
|
633
|
+
style: frame,
|
|
634
|
+
selectable: false,
|
|
635
|
+
// a control the page keeps from assistive technology is kept from
|
|
636
|
+
// it here: core leaves the node, and the widget in it, out of the
|
|
637
|
+
// accessibility tree
|
|
638
|
+
...(ariaHidden(el) && { 'aria-hidden': true }),
|
|
639
|
+
},
|
|
640
|
+
widget,
|
|
641
|
+
),
|
|
590
642
|
);
|
|
591
643
|
}
|
|
592
644
|
|
|
645
|
+
/**
|
|
646
|
+
* What an element's `tabindex` says of its widget's place in the Tab order
|
|
647
|
+
* (HTML 6.6.3): a negative one is focusable — by a press, by its label, by
|
|
648
|
+
* `autofocus` — and not reached by Tab, which is core's `tabIndex={-1}`
|
|
649
|
+
* too. Radix lays a native `<select tabindex="-1" aria-hidden="true">`
|
|
650
|
+
* beside the picker it draws, cut to nothing: it was a stop the eye could
|
|
651
|
+
* not find, and Space on it opened an empty menu.
|
|
652
|
+
*
|
|
653
|
+
* Nothing else is handed over. Zero is where a control already is, and a
|
|
654
|
+
* positive one would put a page's control ahead of the application's own,
|
|
655
|
+
* whose window it is: the order between a document and what is around it
|
|
656
|
+
* is not the document's to set.
|
|
657
|
+
*/
|
|
658
|
+
function tabOrder(el: Element): { tabIndex: -1 } | undefined {
|
|
659
|
+
// the rules for parsing integers (HTML 2.3.4.1): white space, a sign,
|
|
660
|
+
// digits, and whatever follows them ignored
|
|
661
|
+
const m = /^[ \t\n\f\r]*([+-]?\d+)/.exec(attr(el, 'tabindex') ?? '');
|
|
662
|
+
return m && Number(m[1]) < 0 ? { tabIndex: -1 } : undefined;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/** Whether an element is hidden from assistive technology: `aria-hidden`
|
|
666
|
+
* is true on it or on an element around it (WAI-ARIA 1.2, 6.6). */
|
|
667
|
+
function ariaHidden(el: Element): boolean {
|
|
668
|
+
for (let at: Element | null = el; at;) {
|
|
669
|
+
if (attr(at, 'aria-hidden')?.trim().toLowerCase() === 'true') return true;
|
|
670
|
+
const parent: Element['parent'] = at.parent;
|
|
671
|
+
at = isElement(parent) ? parent : null;
|
|
672
|
+
}
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/** How far past its box a widget's focus ring is drawn, and then some. */
|
|
677
|
+
const RING_REACH = 8;
|
|
678
|
+
|
|
593
679
|
/** A field's `maxlength`, which the widget enforces as it is typed into. */
|
|
594
680
|
function maxLength(el: Element): number | undefined {
|
|
595
681
|
const raw = attr(el, 'maxlength')?.trim();
|