@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
package/src/html/index.ts CHANGED
@@ -14,14 +14,7 @@
14
14
  // is a property of the design rather than a setting.
15
15
  import React from 'react';
16
16
  import type { ReactElement, ReactNode } from 'react';
17
- import {
18
- Button,
19
- Checkbox,
20
- Radio,
21
- RadioGroup,
22
- Select,
23
- useTheme,
24
- } from 'react-x11';
17
+ import { useTheme } from 'react-x11';
25
18
  import type { DrawnNode, MouseEvent as X11MouseEvent, Rect } from 'react-x11';
26
19
  import { tint } from 'react-x11/style';
27
20
  import type { Style } from 'react-x11/style';
@@ -31,18 +24,13 @@ import type {} from 'react-x11/jsx-runtime';
31
24
  import { useLinkClicks, useSelectionMenu } from '../richtext/index.js';
32
25
  import { useFontPrewarm } from '../internal/prewarm.js';
33
26
  import { hx } from './hx.js';
34
- import { attr, tagOf } from './dom.js';
35
27
  import type { Document, Element } from './dom.js';
36
28
  import { ELEMENT, HtmlViewNode, registerHtmlView } from './node.js';
37
29
  import type { HtmlViewProps, ScriptRequest } from './node.js';
38
30
  import type { RootLook } from './css/style.js';
39
- import {
40
- buttonLabel,
41
- optionsOf,
42
- selectedOption,
43
- textareaValue,
44
- } from './controls.js';
45
- import type { BareField, ControlRect } from './controls.js';
31
+ import type { ControlRect } from './controls.js';
32
+ import type { FormSubmission } from './form.js';
33
+ import { useForms } from './widgets.js';
46
34
  import type { ResourceRequest, ResourceResult } from './resources.js';
47
35
 
48
36
  export {
@@ -53,6 +41,13 @@ export {
53
41
  export type { HtmlViewProps, ScriptRequest } from './node.js';
54
42
  export type { ResourceRequest, ResourceResult } from './resources.js';
55
43
  export type { BareField, ControlRect } from './controls.js';
44
+ export type {
45
+ FormEnctype,
46
+ FormMethod,
47
+ FormSubmission,
48
+ SubmitContext,
49
+ } from './form.js';
50
+ export { formSubmission } from './form.js';
56
51
  export type { ComputedStyle, RootLook } from './css/style.js';
57
52
  export type {
58
53
  AnyNode,
@@ -154,6 +149,15 @@ export interface HtmlProps {
154
149
  * calls the handle's `refresh()`.
155
150
  */
156
151
  onControlChange?: (element: Element, value: string | boolean) => void;
152
+ /**
153
+ * A form was submitted — a submit button pressed, or Enter in one of its
154
+ * text fields. Handed over as the request it makes: the method, the URL
155
+ * (resolved as `onLink`'s `href` is, and for a GET with the form's entries
156
+ * as its query), a POST's encoded body and its content type, and the
157
+ * entries themselves. Absent means submitting does nothing: this
158
+ * component never sends anything by itself, any more than it navigates.
159
+ */
160
+ onSubmit?: (submission: FormSubmission) => void;
157
161
  /** Base text style. Defaults: theme `fontSize` (14), `sans-serif`. */
158
162
  fontSize?: number;
159
163
  fontFamily?: string;
@@ -230,6 +234,10 @@ function deriveLook(
230
234
  controlPadY: Number(theme.paddingY ?? 6),
231
235
  controlBorder: Number(theme.borderWidth ?? 1),
232
236
  controlRadius: Number(theme.radius ?? 4),
237
+ // not `fontSize` and `fontFamily`, which a host sets to the web's
238
+ // `medium` and the face it reads pages in
239
+ controlFontSize: Number(theme.fontSize ?? 14),
240
+ controlFontFamily: String(theme.fontFamily ?? 'sans-serif'),
233
241
  };
234
242
  }
235
243
 
@@ -285,14 +293,27 @@ export function Html(props: HtmlProps): ReactElement {
285
293
  onScript,
286
294
  onDocument,
287
295
  onControlChange,
296
+ onSubmit,
288
297
  style,
289
298
  } = props;
290
299
 
291
300
  const theme = useTheme() as unknown as Record<string, unknown>;
292
301
  const links = useLinkClicks(onLink);
293
- const buttons = useButtonPresses(onControlChange);
294
302
  const menu = useSelectionMenu(selectable);
295
303
 
304
+ // The element, for the document's base when a form is submitted, as well
305
+ // as wherever the application's `ref` wants it.
306
+ const viewNode = React.useRef<HtmlViewNode | null>(null);
307
+ const outerRef = props.ref;
308
+ const viewRef = React.useCallback(
309
+ (node: HtmlViewNode | null) => {
310
+ viewNode.current = node;
311
+ if (typeof outerRef === 'function') outerRef(node);
312
+ else if (outerRef) (outerRef as React.RefObject<unknown>).current = node;
313
+ },
314
+ [outerRef],
315
+ );
316
+
296
317
  const look = React.useMemo(
297
318
  () => deriveLook(theme, props),
298
319
  [theme, props.fontSize, props.fontFamily, props.monoFamily],
@@ -305,6 +326,13 @@ export function Html(props: HtmlProps): ReactElement {
305
326
  const documentRef = React.useRef<Document | null>(null);
306
327
  const [controls, setControls] = React.useState<ControlRect[]>([]);
307
328
  const [domRevision, setDomRevision] = React.useState(0);
329
+ const forms = useForms({
330
+ view: viewNode,
331
+ baseUrl,
332
+ onControlChange,
333
+ onSubmit,
334
+ touch: () => setDomRevision((n) => n + 1),
335
+ });
308
336
 
309
337
  const handleDocument = React.useCallback(
310
338
  (doc: Document) => {
@@ -338,7 +366,7 @@ export function Html(props: HtmlProps): ReactElement {
338
366
  onDocument: handleDocument,
339
367
  onControls: handleControls,
340
368
  domRevision,
341
- ref: props.ref as React.Ref<unknown>,
369
+ ref: viewRef as React.Ref<unknown>,
342
370
  // grown with the component, where an application grows it: the root's
343
371
  // background covers the whole of it, as a page's covers the window
344
372
  style: { alignSelf: 'stretch', flexGrow: 1 },
@@ -347,16 +375,7 @@ export function Html(props: HtmlProps): ReactElement {
347
375
  const children: ReactNode[] = [
348
376
  h(ELEMENT, { key: 'view', ...viewProps } as Record<string, unknown>),
349
377
  ];
350
- for (const rect of controls) {
351
- children.push(
352
- renderControl(
353
- rect,
354
- look,
355
- () => setDomRevision((n) => n + 1),
356
- onControlChange,
357
- ),
358
- );
359
- }
378
+ children.push(...forms.render(controls, look));
360
379
 
361
380
  const rootStyle: Style = { flexDirection: 'column', position: 'relative' };
362
381
  return hx(
@@ -374,11 +393,11 @@ export function Html(props: HtmlProps): ReactElement {
374
393
  ...links,
375
394
  onMouseDown: (ev: X11MouseEvent<DrawnNode>) => {
376
395
  links.onMouseDown(ev);
377
- buttons.onMouseDown(ev);
396
+ forms.onMouseDown(ev);
378
397
  },
379
398
  onMouseUp: (ev: X11MouseEvent<DrawnNode>) => {
380
399
  links.onMouseUp(ev);
381
- buttons.onMouseUp(ev);
400
+ forms.onMouseUp(ev);
382
401
  },
383
402
  ...menu,
384
403
  'data-testname': props['data-testname'],
@@ -428,276 +447,6 @@ export function useHtmlHandle(): HtmlHandle & { ref: React.Ref<unknown> } {
428
447
  );
429
448
  }
430
449
 
431
- /**
432
- * A press and a release on the same `<button>` the document draws, reported
433
- * as a pressed widget reports one: `onControlChange` with the button and its
434
- * `value`. A `<button>` is laid out and drawn like any box (the UA sheet's
435
- * `button` rule) — its content is the page's, and most of the buttons on
436
- * the web are an icon or a pill of a page's own design — so it is the
437
- * element that finds out it was pressed, not a widget. A disabled one, or
438
- * a press that lands in a link inside one, is not a press of it.
439
- */
440
- function useButtonPresses(onControlChange: HtmlProps['onControlChange']): {
441
- onMouseDown: (ev: X11MouseEvent<DrawnNode>) => void;
442
- onMouseUp: (ev: X11MouseEvent<DrawnNode>) => void;
443
- } {
444
- const press = React.useRef<Element | null>(null);
445
- const buttonAt = (ev: X11MouseEvent<DrawnNode>): Element | null => {
446
- const target = ev.target as {
447
- elementAtPoint?: (x: number, y: number) => Element | null;
448
- } | null;
449
- let node =
450
- typeof target?.elementAtPoint === 'function'
451
- ? target.elementAtPoint(ev.x, ev.y)
452
- : null;
453
- for (
454
- ;
455
- node;
456
- node = node.parent?.type === 'tag' ? (node.parent as Element) : null
457
- ) {
458
- if (tagOf(node) === 'a' && attr(node, 'href') !== undefined) return null;
459
- if (tagOf(node) === 'button') {
460
- return attr(node, 'disabled') === undefined ? node : null;
461
- }
462
- }
463
- return null;
464
- };
465
- return {
466
- onMouseDown: (ev) => {
467
- press.current = onControlChange && ev.button === 1 ? buttonAt(ev) : null;
468
- },
469
- onMouseUp: (ev) => {
470
- const pressed = press.current;
471
- press.current = null;
472
- if (!pressed || !onControlChange || ev.button !== 1) return;
473
- if (buttonAt(ev) !== pressed) return;
474
- onControlChange(pressed, attr(pressed, 'value') ?? '');
475
- },
476
- };
477
- }
478
-
479
- // --- the widgets ------------------------------------------------------------
480
-
481
- /**
482
- * One form control, as a real widget at the rectangle layout reserved for it.
483
- *
484
- * Every one of these is a **core** widget rather than something drawn here:
485
- * a `<select>` in a document drops the same menu as a `<Select>` in the
486
- * window around it, a `<textinput>` gets the same caret, the same IME and the
487
- * same edit menu, and all of them join the window's focus order. The
488
- * alternative — drawing them in the paint pass — would be a picture of a form.
489
- */
490
- function renderControl(
491
- rect: ControlRect,
492
- look: RootLook,
493
- touch: () => void,
494
- onChange: HtmlProps['onControlChange'],
495
- ): ReactNode {
496
- const el = rect.element;
497
- const key = `${rect.kind}:${rect.x},${rect.y}`;
498
- const disabled = attr(el, 'disabled') !== undefined;
499
- const readOnly = attr(el, 'readonly') !== undefined;
500
- // a field whose box the document draws takes its content box
501
- const at = rect.bare ?? rect;
502
- const frame: Style = {
503
- position: 'absolute',
504
- left: Math.round(at.x),
505
- top: Math.round(at.y),
506
- width: Math.round(at.width),
507
- height: Math.round(at.height),
508
- };
509
- const field = rect.bare ? bareField(rect.bare) : fieldChrome(look);
510
- const report = (value: string | boolean): void => {
511
- onChange?.(el, value);
512
- touch();
513
- };
514
- // A text edit does NOT touch: the value lives in the widget and is echoed
515
- // into the DOM attribute, and neither changes any box — while `touch()`
516
- // would re-run the cascade and relayout the whole document *per
517
- // keystroke*. This also matches HTML's own semantics: typing updates the
518
- // value property, not the attribute selectors match against. The
519
- // checkables keep the full touch, because `[checked]` is a selector
520
- // documents really use.
521
- const reportText = (value: string): void => {
522
- onChange?.(el, value);
523
- };
524
-
525
- let widget: ReactNode;
526
- switch (rect.kind) {
527
- case 'checkbox':
528
- widget = h(Checkbox, {
529
- checked: attr(el, 'checked') !== undefined,
530
- disabled,
531
- onChange: (ev) => {
532
- const next = ev.value;
533
- if (next) el.attribs.checked = '';
534
- else delete el.attribs.checked;
535
- report(next);
536
- },
537
- });
538
- break;
539
- case 'radio': {
540
- // Core's radio is a group member and HTML's is a free-standing input
541
- // that happens to share a `name`. Each one is therefore its own
542
- // one-member `RadioGroup`, and the exclusivity that makes it a group
543
- // is done where HTML actually keeps it: in the DOM.
544
- const value = attr(el, 'value') ?? 'on';
545
- widget = h(
546
- RadioGroup,
547
- {
548
- value: attr(el, 'checked') !== undefined ? value : undefined,
549
- onChange: () => {
550
- clearRadioGroup(el);
551
- el.attribs.checked = '';
552
- report(value);
553
- },
554
- },
555
- h(Radio, { key: 'r', value, disabled }),
556
- );
557
- break;
558
- }
559
- case 'button':
560
- widget = h(Button, {
561
- label: buttonLabel(el),
562
- disabled,
563
- style: { width: '100%', height: '100%' },
564
- onPress: () => report(attr(el, 'value') ?? ''),
565
- });
566
- break;
567
- case 'select': {
568
- const options = optionsOf(el);
569
- widget = h(Select, {
570
- options: options.map((o) => ({ value: o.value, label: o.label })),
571
- value: selectedOption(el) ?? undefined,
572
- disabled,
573
- style: { width: '100%', height: '100%' },
574
- onChange: (ev) => {
575
- const next = String(ev.value ?? '');
576
- setSelectedOption(el, next);
577
- report(next);
578
- },
579
- });
580
- break;
581
- }
582
- case 'textarea':
583
- // Uncontrolled on purpose: the widget owns the live text the way a
584
- // browser's does, and a re-render from any other cause remounts it
585
- // with whatever was last echoed into the DOM.
586
- widget = hx('textarea', {
587
- defaultValue: textareaValue(el),
588
- style: [field, { width: '100%', height: '100%' }],
589
- onChange: readOnly ? undefined : (ev) => reportText(ev.value),
590
- });
591
- break;
592
- case 'input': {
593
- const type = (attr(el, 'type') ?? 'text').toLowerCase();
594
- widget = hx('textinput', {
595
- defaultValue: attr(el, 'value') ?? '',
596
- placeholder: attr(el, 'placeholder'),
597
- // Core's word for a password field: nothing in it reaches a
598
- // selection, PRIMARY included.
599
- sensitive: type === 'password',
600
- style: [field, { width: '100%', height: '100%' }],
601
- onChange: readOnly
602
- ? undefined
603
- : (ev) => {
604
- el.attribs.value = ev.value;
605
- reportText(ev.value);
606
- },
607
- });
608
- break;
609
- }
610
- default:
611
- return null;
612
- }
613
- return hx('box', { key, style: frame, selectable: false }, widget);
614
- }
615
-
616
- /**
617
- * The chrome a text field needs.
618
- *
619
- * `<textinput>` and `<textarea>` are core *elements* rather than components,
620
- * so they draw no frame of their own — an application supplies one, which is
621
- * why core's own `<Button>` and `<Select>` are components and these are not.
622
- * The values are the palette's, so a field in a document and a `<Select>`
623
- * beside it are the same height with the same corner and the same edge.
624
- */
625
- function fieldChrome(look: RootLook): Style {
626
- return {
627
- backgroundColor: look.surface,
628
- borderWidth: look.controlBorder,
629
- borderColor: look.borderColor,
630
- borderRadius: look.controlRadius,
631
- paddingLeft: 6,
632
- paddingRight: 6,
633
- color: look.color,
634
- fontFamily: look.fontFamily,
635
- fontSize: look.fontSize,
636
- };
637
- }
638
-
639
- /**
640
- * A text field whose box the author styled: the document draws the border
641
- * and the background, so the widget draws neither, and its text is the
642
- * element's colour and font, which the author chose to go on that
643
- * background, rather than the theme's.
644
- */
645
- function bareField(bare: BareField): Style {
646
- return {
647
- backgroundColor: 'transparent',
648
- borderWidth: 0,
649
- borderRadius: 0,
650
- paddingLeft: 0,
651
- paddingRight: 0,
652
- color: bare.color,
653
- fontFamily: bare.fontFamily,
654
- fontSize: bare.fontSize,
655
- };
656
- }
657
-
658
- function clearRadioGroup(el: Element): void {
659
- const name = attr(el, 'name');
660
- if (!name) return;
661
- let root: Element | null = el;
662
- while (root?.parent && root.parent.type === 'tag')
663
- root = root.parent as Element;
664
- if (!root) return;
665
- const walk = (node: Element): void => {
666
- for (const child of node.children) {
667
- if (child.type !== 'tag') continue;
668
- const tag = child.name.toLowerCase();
669
- if (
670
- tag === 'input' &&
671
- (child.attribs.type ?? '').toLowerCase() === 'radio' &&
672
- child.attribs.name === name
673
- ) {
674
- delete child.attribs.checked;
675
- }
676
- walk(child);
677
- }
678
- };
679
- walk(root);
680
- }
681
-
682
- function setSelectedOption(el: Element, value: string): void {
683
- const walk = (node: Element): void => {
684
- for (const child of node.children) {
685
- if (child.type !== 'tag') continue;
686
- if (child.name.toLowerCase() === 'option') {
687
- let label = '';
688
- for (const kid of child.children) {
689
- if (kid.type === 'text') label += kid.data;
690
- }
691
- const own = child.attribs.value ?? label.trim();
692
- if (own === value) child.attribs.selected = '';
693
- else delete child.attribs.selected;
694
- }
695
- walk(child);
696
- }
697
- };
698
- walk(el);
699
- }
700
-
701
450
  declare module 'react-x11/jsx-runtime' {
702
451
  namespace JSX {
703
452
  interface IntrinsicElements {