react-x11 2.11.0 → 2.12.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.
- package/README.md +278 -129
- package/package.json +10 -3
- package/src/Reconciler.js +15 -17
- package/src/a11y.js +2 -2
- package/src/anchor.js +7 -5
- package/src/bootstrap.js +14 -0
- package/src/clientmessage.js +1 -1
- package/src/cocoa/app.js +292 -49
- package/src/cocoa/bezels.js +175 -30
- package/src/cocoa/dnd.js +27 -13
- package/src/cocoa/fonts.js +3 -3
- package/src/cocoa/glarea.js +20 -3
- package/src/cocoa/main.d.ts +8 -0
- package/src/cocoa/main.js +43 -0
- package/src/cocoa/panehost.js +15 -5
- package/src/cocoa/presenter.js +13 -9
- package/src/cocoa/promotion.js +4 -7
- package/src/cocoa/relaunch.js +207 -0
- package/src/cocoa/threaded.js +246 -0
- package/src/cocoa/window.js +256 -42
- package/src/components/Select.js +2 -2
- package/src/components/anchor.js +3 -3
- package/src/components/native.js +12 -7
- package/src/components/theme.js +2 -2
- package/src/debug.js +1 -1
- package/src/decorations.js +1 -1
- package/src/editmenu.js +2 -2
- package/src/errors.js +46 -0
- package/src/events.js +6 -6
- package/src/foreignnodes.js +3 -2
- package/src/frames.js +2 -2
- package/src/glnodes.js +1 -1
- package/src/grid.js +1653 -0
- package/src/host.d.ts +230 -0
- package/src/host.js +11 -3
- package/src/imagesource.js +1 -1
- package/src/index.d.ts +21 -4
- package/src/index.js +9 -1
- package/src/layouts.js +721 -0
- package/src/node.d.ts +4 -2
- package/src/node.js +19 -21
- package/src/nodes/animation.js +644 -0
- package/src/nodes/box.js +21 -0
- package/src/nodes/boxpaint.js +473 -0
- package/src/nodes/canvas.js +269 -0
- package/src/nodes/cascade.js +600 -0
- package/src/nodes/damage.js +183 -0
- package/src/nodes/edithistory.js +124 -0
- package/src/nodes/editmenupopup.js +260 -0
- package/src/nodes/hittest.js +185 -0
- package/src/nodes/image.js +266 -0
- package/src/nodes/install.js +75 -0
- package/src/nodes/invalidate.js +465 -0
- package/src/nodes/kinds.js +31 -0
- package/src/nodes/layout.js +439 -0
- package/src/nodes/layouthost.js +949 -0
- package/src/nodes/node.js +868 -0
- package/src/nodes/paint.js +466 -0
- package/src/nodes/position.js +366 -0
- package/src/nodes/preedit.js +127 -0
- package/src/nodes/queries.js +330 -0
- package/src/nodes/rects.js +102 -0
- package/src/nodes/scrollable.js +891 -0
- package/src/nodes/scrollbars.js +138 -0
- package/src/nodes/scrollblit.js +1034 -0
- package/src/nodes/selectable.js +142 -0
- package/src/nodes/styling.js +225 -0
- package/src/nodes/text.js +649 -0
- package/src/nodes/textarea.js +391 -0
- package/src/nodes/textinput.js +1146 -0
- package/src/nodes/util.js +17 -0
- package/src/nodes/window/anchoring.js +161 -0
- package/src/nodes/window/capabilities.js +190 -0
- package/src/nodes/window/debugpaint.js +83 -0
- package/src/nodes/window/droptarget.js +145 -0
- package/src/nodes/window/floors.js +577 -0
- package/src/nodes/window/flush.js +334 -0
- package/src/nodes/window/hints.js +482 -0
- package/src/nodes/window/listeners.js +222 -0
- package/src/nodes/window/popup.js +71 -0
- package/src/nodes/window/size.js +591 -0
- package/src/nodes/window/window.js +945 -0
- package/src/palette.js +1 -1
- package/src/registry.js +7 -3
- package/src/styles.js +137 -15
- package/src/svgnodes.js +2 -1
- package/src/testing/harness.js +2 -2
- package/src/textselection.js +5 -3
- package/src/trace-registry.js +1 -1
- package/src/types/components.d.ts +38 -6
- package/src/types/elements.d.ts +11 -1
- package/src/types/nodes.d.ts +17 -2
- package/src/types/style.d.ts +94 -3
- package/src/windowstate.js +1 -1
- package/src/yoga.js +1 -1
- package/src/nodes.js +0 -13120
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// The text an element answers for (#259) and being a selection surface: what
|
|
2
|
+
// a reader can select and copy out of a node, and the accessors every
|
|
3
|
+
// text-bearing element implements.
|
|
4
|
+
|
|
5
|
+
import { TextSelection } from '../textselection.js';
|
|
6
|
+
|
|
7
|
+
/** Text and selection, installed onto `Node.prototype` by node.js. */
|
|
8
|
+
export class NodeSelectable {
|
|
9
|
+
// --- the text an element answers for (issue #259) ------------------------
|
|
10
|
+
//
|
|
11
|
+
// Four questions, in one index space and one coordinate space: characters
|
|
12
|
+
// are **code points** (an emoji is one position, not two — the space ntk's
|
|
13
|
+
// caret API speaks), and rectangles are in the owning window's coordinates,
|
|
14
|
+
// the same ones `abs`, `contentBox()` and a mouse event's `x`/`y` are in.
|
|
15
|
+
//
|
|
16
|
+
// They are what a selection is made of, and the reason they are on `Node`
|
|
17
|
+
// rather than on `<text>`: the selection service walks a subtree and asks,
|
|
18
|
+
// so an element that answers them joins a document without core knowing it
|
|
19
|
+
// exists. The defaults are the honest answers for an element with no text
|
|
20
|
+
// — a `<box>` has none, and `null` says so rather than claiming an empty
|
|
21
|
+
// string sits somewhere inside it.
|
|
22
|
+
|
|
23
|
+
/** This element's text, or null when it has none — the string the three
|
|
24
|
+
* accessors below index into. */
|
|
25
|
+
textContent() {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The character boundary nearest a point, in window coordinates. Clamps,
|
|
30
|
+
* so a point past the end of the text answers with the end of it. */
|
|
31
|
+
textIndexAt(x, y) {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Where a caret at this index would stand, in window coordinates — a
|
|
36
|
+
* zero-width rect from the top of the glyphs to the bottom of them. */
|
|
37
|
+
textCaretRect(index) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The bands a highlight over `[start, end)` fills, in window coordinates:
|
|
43
|
+
* one per line, and more than one on a line whose text changes direction
|
|
44
|
+
* halfway across it. Empty when the range is empty or off the end.
|
|
45
|
+
*/
|
|
46
|
+
textRangeRects(start, end) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The part of this element's text the document selection covers, as
|
|
52
|
+
* `{ start, end }` in code points, or null. An element that paints its own
|
|
53
|
+
* text paints a band under it while this is set — `textRangeRects` gives
|
|
54
|
+
* the rectangles and `selectionColor` the fill — and that is the whole of
|
|
55
|
+
* taking part in a selection. `<text>` keeps no more state than this.
|
|
56
|
+
*/
|
|
57
|
+
get selectionRange() {
|
|
58
|
+
return this._selRange
|
|
59
|
+
? { start: this._selRange.start, end: this._selRange.end }
|
|
60
|
+
: null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** What to fill `textRangeRects` with while `selectionRange` is set: the
|
|
64
|
+
* surface's `selectionColor`, or the theme's accent tinted. */
|
|
65
|
+
get selectionColor() {
|
|
66
|
+
return this._selRange?.color ?? null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// --- being a selection surface -------------------------------------------
|
|
70
|
+
|
|
71
|
+
/** `selectable` arrived or left. `true` makes this element the surface a
|
|
72
|
+
* drag inside it selects across; `false` opts its subtree out of the one
|
|
73
|
+
* above it, and is read where the surface is looked up. */
|
|
74
|
+
_syncSelectable(props) {
|
|
75
|
+
const wanted = props.selectable === true;
|
|
76
|
+
if (wanted === Boolean(this._textSelection)) return;
|
|
77
|
+
if (wanted) {
|
|
78
|
+
this._textSelection = new TextSelection(this);
|
|
79
|
+
// The I-beam is what says the text here can be taken. A surface is
|
|
80
|
+
// also a focus target — a11y.js reads the same prop — because Ctrl+C
|
|
81
|
+
// is a keystroke and a keystroke has to arrive somewhere.
|
|
82
|
+
this.defaultCursor ??= 'text';
|
|
83
|
+
} else {
|
|
84
|
+
this._textSelection.destroy();
|
|
85
|
+
this._textSelection = null;
|
|
86
|
+
if (this.defaultCursor === 'text') this.defaultCursor = undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The document selection this element owns, or null: a snapshot of
|
|
92
|
+
* `{ isCollapsed, text, ranges }`, not a live object.
|
|
93
|
+
*
|
|
94
|
+
* Named for the text rather than called `selection`, because a base class
|
|
95
|
+
* that claims a plain noun claims it from every element built on it — and
|
|
96
|
+
* `this.selection = ...` in a subclass constructor is then a TypeError
|
|
97
|
+
* against a getter, which is a bad way to find out.
|
|
98
|
+
*/
|
|
99
|
+
get textSelection() {
|
|
100
|
+
const selection = this._textSelection;
|
|
101
|
+
if (!selection) return null;
|
|
102
|
+
return {
|
|
103
|
+
isCollapsed: selection.isCollapsed,
|
|
104
|
+
text: selection.text(),
|
|
105
|
+
ranges: [...selection.ranges].map(([node, [start, end]]) => ({
|
|
106
|
+
node,
|
|
107
|
+
start,
|
|
108
|
+
end,
|
|
109
|
+
})),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Select everything in this surface, and take PRIMARY with it. */
|
|
114
|
+
selectAll() {
|
|
115
|
+
this._textSelection?.selectAll();
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Drop the selection in this surface. PRIMARY is left where it is: the
|
|
120
|
+
* text stays pasteable, which is what every other X client does. */
|
|
121
|
+
clearSelection() {
|
|
122
|
+
this._textSelection?.clear();
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** What a copy would put on the clipboard. */
|
|
127
|
+
selectedText() {
|
|
128
|
+
return this._textSelection?.text() ?? '';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Set both ends by hand — `{ node, index }` each, indices in code points.
|
|
132
|
+
* `setSelection(null)` is `clearSelection()`. */
|
|
133
|
+
setSelection(anchor, focus = anchor) {
|
|
134
|
+
this._textSelection?.setSelection(anchor, focus);
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Another surface is showing the app's selection now. */
|
|
139
|
+
_selectionLost() {
|
|
140
|
+
this._textSelection?.lost();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// A node's own style: `props.style` resolved against its states, tokens,
|
|
2
|
+
// queries and scale into `this.style`, and the style names an element
|
|
3
|
+
// claims as its own semantics.
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
flattenStyle,
|
|
7
|
+
validateStyle,
|
|
8
|
+
resolveStyleStates,
|
|
9
|
+
resolveComputedStyle,
|
|
10
|
+
scaleResolvedStyle,
|
|
11
|
+
hasStateStyles,
|
|
12
|
+
isStyleProp,
|
|
13
|
+
EMPTY_STYLE,
|
|
14
|
+
styleUsesTokens,
|
|
15
|
+
resolveTokens,
|
|
16
|
+
queryKinds,
|
|
17
|
+
QUERY_SIZE,
|
|
18
|
+
QUERY_SUPPORTS,
|
|
19
|
+
QUERY_CONTAINER,
|
|
20
|
+
containerQueryNames,
|
|
21
|
+
containerAnswers,
|
|
22
|
+
resolveQueries,
|
|
23
|
+
} from '../styles.js';
|
|
24
|
+
import { CUSTOM_SEMANTIC_NAMES } from './kinds.js';
|
|
25
|
+
import { DEV, shallowEqual } from './util.js';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A style property passed flat used to be silently dropped — it was neither
|
|
29
|
+
* a layout prop, a paint prop nor an `on*` handler, so nothing looked at it
|
|
30
|
+
* and nothing said so. Now that style has its own channel there is exactly
|
|
31
|
+
* one place it can go, and the wrong place is an error that names the fix.
|
|
32
|
+
*/
|
|
33
|
+
function assertNoFlatStyleProps(props, kind, semantic) {
|
|
34
|
+
if (!DEV) return;
|
|
35
|
+
for (const key of Object.keys(props)) {
|
|
36
|
+
if (!isStyleProp(key) || semantic.has(key)) continue;
|
|
37
|
+
throw new Error(
|
|
38
|
+
`react-x11: <${kind} ${key}=…> is a style property — pass it in ` +
|
|
39
|
+
`style: <${kind} style={{ ${key}: … }} />`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Names an element owns as semantics, which therefore never mean style on
|
|
45
|
+
// it. `<window width>` is the X window's width; `<box width>` would be yoga
|
|
46
|
+
// style, and there is no element where a name means both.
|
|
47
|
+
const NO_SEMANTIC_NAMES = new Set();
|
|
48
|
+
|
|
49
|
+
/** A node's own style, installed onto `Node.prototype` by node.js. */
|
|
50
|
+
export class NodeStyling {
|
|
51
|
+
/**
|
|
52
|
+
* Everything that paints or lays out reads `this.style`, never `this.props`
|
|
53
|
+
* — props carry element semantics (`title`, `value`, geometry, handlers)
|
|
54
|
+
* and style carries the CSS-like vocabulary, with no name shared between
|
|
55
|
+
* them. `baseStyle` is the flattened `style` prop; `style` is that with
|
|
56
|
+
* the active state blocks overlaid.
|
|
57
|
+
*/
|
|
58
|
+
_syncStyle(props, mounting = false) {
|
|
59
|
+
if (DEV && this.stylable) {
|
|
60
|
+
assertNoFlatStyleProps(props, this.kind, this.semanticNames);
|
|
61
|
+
validateStyle(flattenStyle(props.style), `<${this.kind} style>`);
|
|
62
|
+
}
|
|
63
|
+
this._baseStyle = this.stylable ? flattenStyle(props.style) : EMPTY_STYLE;
|
|
64
|
+
this._usesTokens = this.stylable && styleUsesTokens(this._baseStyle);
|
|
65
|
+
if (this._usesTokens) {
|
|
66
|
+
const theme = this.theme;
|
|
67
|
+
const strict = this.placed;
|
|
68
|
+
const problems = strict ? [] : null;
|
|
69
|
+
this._baseStyle = resolveTokens(
|
|
70
|
+
this._baseStyle,
|
|
71
|
+
theme,
|
|
72
|
+
`<${this.kind} style>`,
|
|
73
|
+
strict,
|
|
74
|
+
problems,
|
|
75
|
+
);
|
|
76
|
+
if (problems?.length) this._tokenProblem(problems, mounting);
|
|
77
|
+
}
|
|
78
|
+
// `disabled` is a prop, not something the pointer does, so it is read
|
|
79
|
+
// straight off props rather than driven by the event manager
|
|
80
|
+
this.states[':disabled'] = Boolean(props.disabled);
|
|
81
|
+
// window size queries fold into the base before state blocks, so a
|
|
82
|
+
// `:hover` inside the wide layout still wins over the wide layout
|
|
83
|
+
const kinds = queryKinds(this._baseStyle);
|
|
84
|
+
const queried = (kinds & QUERY_SIZE) !== 0;
|
|
85
|
+
if (queried !== this._queried) {
|
|
86
|
+
this._queried = queried;
|
|
87
|
+
const root = this.root;
|
|
88
|
+
if (root?._sizeQueryNodes) {
|
|
89
|
+
if (queried) root._sizeQueryNodes.add(this);
|
|
90
|
+
else root._sizeQueryNodes.delete(this);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// `@supports` blocks keep their own registry: what re-resolves them is
|
|
94
|
+
// the server's answer changing, not a resize
|
|
95
|
+
// Registered unconditionally rather than on change: a `<window>`'s own
|
|
96
|
+
// style is resolved by the Node constructor, before `root` is even
|
|
97
|
+
// assigned, so the first pass has nowhere to register and a "did it
|
|
98
|
+
// change" guard would keep it unregistered forever. Set.add is
|
|
99
|
+
// idempotent and these blocks are rare.
|
|
100
|
+
const asks = (kinds & QUERY_SUPPORTS) !== 0;
|
|
101
|
+
this._supportsQueried = asks;
|
|
102
|
+
if (this.root?._supportsQueryNodes) {
|
|
103
|
+
if (asks) this.root._supportsQueryNodes.add(this);
|
|
104
|
+
else this.root._supportsQueryNodes.delete(this);
|
|
105
|
+
}
|
|
106
|
+
// Attention candidates keep a registry for the same reason and are
|
|
107
|
+
// registered the same unconditional way — a `<window>`'s own style is
|
|
108
|
+
// resolved before `root` is assigned, so a "did it change" guard would
|
|
109
|
+
// leave it unregistered forever. Unlike hover, attention is *matched*
|
|
110
|
+
// against this set rather than hit-tested, so a node that is not in it
|
|
111
|
+
// is not a candidate at all.
|
|
112
|
+
const wantsAttention = Boolean(
|
|
113
|
+
props.unstable_onAttention || this._baseStyle[':attention'],
|
|
114
|
+
);
|
|
115
|
+
this._wantsAttention = wantsAttention;
|
|
116
|
+
if (this.root?._attentionNodes) {
|
|
117
|
+
if (wantsAttention) this.root._attentionNodes.add(this);
|
|
118
|
+
else this.root._attentionNodes.delete(this);
|
|
119
|
+
}
|
|
120
|
+
// `@container` blocks keep a third registry: what re-resolves them is a
|
|
121
|
+
// layout pass moving the container they ask about — neither a resize
|
|
122
|
+
// nor the server's answer. The record exists only on the nodes that
|
|
123
|
+
// ask, so every other node pays one bit test here and nothing below.
|
|
124
|
+
// Insertion registers through `_registerSizeQueries`; this is for a
|
|
125
|
+
// style that starts or stops asking on a node already in a window.
|
|
126
|
+
const asksContainers = (kinds & QUERY_CONTAINER) !== 0;
|
|
127
|
+
let containers = null;
|
|
128
|
+
if (asksContainers) {
|
|
129
|
+
const base = this._baseStyle;
|
|
130
|
+
let cq = this._cq;
|
|
131
|
+
if (cq === null || cq.style !== base) {
|
|
132
|
+
if (cq === null) {
|
|
133
|
+
cq = this._cq = {
|
|
134
|
+
style: base,
|
|
135
|
+
names: null,
|
|
136
|
+
containers: null,
|
|
137
|
+
answers: '',
|
|
138
|
+
pin: null,
|
|
139
|
+
warned: false,
|
|
140
|
+
};
|
|
141
|
+
} else {
|
|
142
|
+
// a different style asks different questions, and a pin held for
|
|
143
|
+
// the old one is not an answer to the new one
|
|
144
|
+
cq.style = base;
|
|
145
|
+
cq.pin = null;
|
|
146
|
+
}
|
|
147
|
+
cq.names = containerQueryNames(base);
|
|
148
|
+
}
|
|
149
|
+
this.root?._containerQueryNodes?.add(this);
|
|
150
|
+
containers = this._pinnedContainerSizes();
|
|
151
|
+
} else if (this._cq !== null) {
|
|
152
|
+
this._cq = null;
|
|
153
|
+
this.root?._containerQueryNodes?.delete(this);
|
|
154
|
+
}
|
|
155
|
+
if (queried || asks || asksContainers) {
|
|
156
|
+
this._baseStyle = resolveQueries(this._baseStyle, {
|
|
157
|
+
size: this.root?.querySize ?? null,
|
|
158
|
+
// null before the window is realized, which reads as "not
|
|
159
|
+
// supported" — the fallback design is the one that works everywhere
|
|
160
|
+
supports: this.root?.capabilities ?? null,
|
|
161
|
+
containers,
|
|
162
|
+
});
|
|
163
|
+
if (asksContainers) {
|
|
164
|
+
this._cq.containers = containers;
|
|
165
|
+
this._cq.answers = containerAnswers(this._baseStyle, containers);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
this._stateful = hasStateStyles(this._baseStyle);
|
|
169
|
+
// The scale multiplies *after* every merge — state blocks, queries,
|
|
170
|
+
// the flex shorthand — so each of those keeps thinking in the logical
|
|
171
|
+
// pixels the app wrote, and device pixels exist only downstream of
|
|
172
|
+
// this line (src/scale.js).
|
|
173
|
+
return this._retarget(
|
|
174
|
+
scaleResolvedStyle(
|
|
175
|
+
resolveComputedStyle(
|
|
176
|
+
this._stateful
|
|
177
|
+
? resolveStyleStates(this._baseStyle, this.states)
|
|
178
|
+
: this._baseStyle,
|
|
179
|
+
),
|
|
180
|
+
this.scale,
|
|
181
|
+
),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* A node state changed (hover, focus, press). Only nodes that actually
|
|
187
|
+
* declare a block for it do anything, and what they do is a repaint —
|
|
188
|
+
* no React render, no reflow, since state blocks cannot touch layout.
|
|
189
|
+
*/
|
|
190
|
+
setStyleState(name, on) {
|
|
191
|
+
if (this.states[name] === on) return;
|
|
192
|
+
this.states[name] = on;
|
|
193
|
+
// the focus ring's reach is read off `:focus-visible` whether or not
|
|
194
|
+
// the node has a state block of its own (`_outlineExtent`)
|
|
195
|
+
this._clearPaintBounds();
|
|
196
|
+
if (!this._stateful || this.destroyed) return;
|
|
197
|
+
const next = scaleResolvedStyle(
|
|
198
|
+
resolveComputedStyle(resolveStyleStates(this._baseStyle, this.states)),
|
|
199
|
+
this.scale,
|
|
200
|
+
);
|
|
201
|
+
if (shallowEqual(next, this._targetStyle)) return;
|
|
202
|
+
this._retarget(next);
|
|
203
|
+
// a state block may only set paint properties, so the node's own region
|
|
204
|
+
// is the whole of what changed
|
|
205
|
+
this.root?.invalidate(false, this, 'style-state');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Style names this element claims as its own semantics (see WindowNode).
|
|
209
|
+
* Registered elements declare theirs to `registerElement`, so the common
|
|
210
|
+
* case needs no subclass. */
|
|
211
|
+
get semanticNames() {
|
|
212
|
+
return CUSTOM_SEMANTIC_NAMES.get(this.kind) ?? NO_SEMANTIC_NAMES;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Whether this element is styled at all. The 3D scene elements and the
|
|
217
|
+
* declarative SVG children are not: they carry their own vocabularies —
|
|
218
|
+
* `position`, `color`, `width` mean a transform, a material and a radius
|
|
219
|
+
* there — so the style channel does not apply to them, the same way it
|
|
220
|
+
* does not apply to an `<input type>` in the DOM.
|
|
221
|
+
*/
|
|
222
|
+
get stylable() {
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
}
|