@raylang/elements 0.6.0 → 0.8.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 (61) hide show
  1. package/base.css +1 -1
  2. package/check/check.d.ts +2 -0
  3. package/check/check.js +18 -4
  4. package/check/cli.js +10 -6
  5. package/components/accordion/accordion.js +8 -5
  6. package/components/alert/alert.js +3 -2
  7. package/components/attachment/attachment.js +5 -6
  8. package/components/avatar/avatar.js +1 -1
  9. package/components/blockquote/blockquote.js +2 -2
  10. package/components/bottom-nav/bottom-nav.js +5 -3
  11. package/components/breadcrumbs/breadcrumbs.js +1 -1
  12. package/components/carousel/carousel.js +26 -14
  13. package/components/checkbox/checkbox.js +6 -3
  14. package/components/checkbox-group/checkbox-group.js +1 -1
  15. package/components/code/code.js +9 -7
  16. package/components/color-picker/color-picker.js +6 -6
  17. package/components/combobox/combobox.js +5 -4
  18. package/components/command-palette/command-palette.js +2 -2
  19. package/components/date-picker/date-picker.js +9 -7
  20. package/components/dialog/dialog.js +2 -2
  21. package/components/drawer/drawer.js +28 -10
  22. package/components/file-upload/file-upload.js +1 -1
  23. package/components/icon/icon.js +9 -1
  24. package/components/indicator/indicator.js +2 -1
  25. package/components/menu/menu.js +1 -1
  26. package/components/nav-link/nav-link.js +6 -3
  27. package/components/notification/notification.js +16 -13
  28. package/components/number-input/number-input.js +1 -1
  29. package/components/pagination/pagination.js +4 -4
  30. package/components/password-input/password-input.js +2 -2
  31. package/components/progress/progress.js +2 -2
  32. package/components/radio/radio.js +4 -2
  33. package/components/scroll-area/scroll-area.js +14 -11
  34. package/components/search/search.js +5 -5
  35. package/components/segmented-control/segmented-control.js +5 -3
  36. package/components/select/select.js +5 -5
  37. package/components/separator/separator.js +8 -3
  38. package/components/sidebar/sidebar.js +14 -6
  39. package/components/slider/slider.js +5 -2
  40. package/components/split/split.js +12 -6
  41. package/components/stat/stat.js +1 -1
  42. package/components/step-dots/step-dots.js +11 -2
  43. package/components/stepper/stepper.js +5 -5
  44. package/components/switch/switch.js +3 -3
  45. package/components/table/table.js +6 -6
  46. package/components/tabs/tabs.js +11 -8
  47. package/components/theme-toggle/theme-toggle.js +2 -2
  48. package/components/timeline/timeline.js +13 -8
  49. package/components/toggle-group/toggle-group.js +6 -4
  50. package/core/core.js +10 -4
  51. package/core/direction.js +19 -0
  52. package/core/icons.js +107 -8
  53. package/core/position.js +11 -3
  54. package/core/styles.js +1 -1
  55. package/core.js +1 -0
  56. package/custom-elements.json +151 -79
  57. package/llms-full.txt +75 -31
  58. package/llms.txt +32 -3
  59. package/package.json +1 -1
  60. package/ray-elements.d.ts +56 -39
  61. package/vscode.html-custom-data.json +1102 -39
package/core/icons.js CHANGED
@@ -6,6 +6,20 @@
6
6
  //
7
7
  // setIcons() also adds new names, for <ray-icon name="…">. Icons are SVG
8
8
  // markup (or an SVGElement) drawn with currentColor; components size them.
9
+ //
10
+ // The components ask for roles, not glyphs: "ui.close" (a dialog's close
11
+ // button), "ui.remove" (a chip's ×), "ui.clear" (a field's ×)… Each role
12
+ // draws its glyph (ALIASES) unless the app registers the role itself, so
13
+ // setIcons({ close: … }) still changes every × and setIcons({ "ui.remove": … })
14
+ // only the chips'.
15
+ //
16
+ // The roles that point along the text (ui.next, ui.previous, ui.disclosure…;
17
+ // MIRRORED) are drawn mirrored in a right-to-left context, the glyph or the
18
+ // app's own icon alike: the next page is to the left there.
19
+ //
20
+ // An icon may have a filled version, "<name>:filled", for a state: the active
21
+ // tab, a favorite, unread. <ray-icon name="star" filled> draws it, and the
22
+ // outline one when there is none. setIcons({ "star:filled": … }) adds one.
9
23
  // The markup is inserted as HTML: register icons from your own code only,
10
24
  // never from user input.
11
25
 
@@ -62,12 +76,80 @@ const DEFAULTS = {
62
76
  "message-circle": svg(`<path d="M20 11.5a8 8 0 0 1-11.6 7.2L4 20l1.3-4.2A8 8 0 1 1 20 11.5z"/>`),
63
77
  };
64
78
 
65
- const registry = new Map(Object.entries(DEFAULTS));
79
+ // The filled versions: the outline's shapes filled (same silhouette: the
80
+ // stroke stays), or one even-odd path where a mark is cut out of a disc.
81
+ const solid = (body) =>
82
+ `<svg viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${body}</svg>`;
83
+ const cut = (d) => `<svg viewBox="0 0 24 24" fill="currentColor" fill-rule="evenodd" aria-hidden="true"><path d="${d}"/></svg>`;
84
+
85
+ /** The filled versions of the default set (registered as "<name>:filled"). */
86
+ const FILLED = {
87
+ "user": solid(`<circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0z"/>`),
88
+ "info": cut("M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 15h-2v-6h2zm0-8h-2V7h2z"),
89
+ "circle-check": cut("M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm-1.5 14.5-4-4 1.4-1.4 2.6 2.6 5.6-5.6 1.4 1.4z"),
90
+ "alert-triangle": cut("M10.3 3.2 1.6 18.1a2 2 0 0 0 1.7 3h17.4a2 2 0 0 0 1.7-3L13.7 3.2a2 2 0 0 0-3.4 0zM11 8.5h2v5.5h-2zM12 16a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5z"),
91
+ "circle-x": cut("M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zM14.12 8.46 15.54 9.88 13.41 12 15.54 14.12 14.12 15.54 12 13.41 9.88 15.54 8.46 14.12 10.59 12 8.46 9.88 9.88 8.46 12 10.59z"),
92
+ "folder": solid(`<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>`),
93
+ bell: solid(`<path d="M10.3 21a1.9 1.9 0 0 0 3.4 0z"/><path d="M3.3 15.3A1 1 0 0 0 4 17h16a1 1 0 0 0 .7-1.7C19.4 14 18 12.6 18 8A6 6 0 0 0 6 8c0 4.6-1.4 6-2.7 7.3"/>`),
94
+ "star": solid(`<path d="M12 3.5l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 16.9l-5.2 2.7 1-5.8-4.3-4.1 5.9-.9z"/>`),
95
+ "settings": solid(`<path d="M4 6h10M18 6h2M4 12h4M12 12h8M4 18h12"/><circle cx="16" cy="6" r="2"/><circle cx="10" cy="12" r="2"/><circle cx="18" cy="18" r="2"/>`),
96
+ "home": solid(`<path d="M4 10.5 12 4l8 6.5V19a1 1 0 0 1-1 1h-4.5v-5.5h-5V20H5a1 1 0 0 1-1-1z"/>`),
97
+ "message-circle": solid(`<path d="M20 11.5a8 8 0 0 1-11.6 7.2L4 20l1.3-4.2A8 8 0 1 1 20 11.5z"/>`),
98
+ };
99
+
100
+ const FILLED_SUFFIX = ":filled";
101
+
102
+ /** The roles the components draw, and the glyph each one uses by default. */
103
+ const ALIASES = {
104
+ "ui.close": "close",
105
+ "ui.clear": "close",
106
+ "ui.remove": "close",
107
+ "ui.expand": "chevron-down",
108
+ "ui.disclosure": "chevron-right",
109
+ "ui.previous": "chevron-left",
110
+ "ui.next": "chevron-right",
111
+ "ui.first": "chevrons-left",
112
+ "ui.last": "chevrons-right",
113
+ "ui.separator": "chevron-right",
114
+ "ui.increment": "chevron-up",
115
+ "ui.decrement": "chevron-down",
116
+ "ui.sort": "chevron-up",
117
+ "ui.checked": "check",
118
+ "ui.indeterminate": "minus",
119
+ "ui.copy": "copy",
120
+ "ui.copied": "check",
121
+ "ui.show-password": "eye",
122
+ "ui.hide-password": "eye-off",
123
+ "ui.play": "play",
124
+ "ui.pause": "pause",
125
+ "ui.search": "search",
126
+ "ui.calendar": "calendar",
127
+ "ui.eyedropper": "pipette",
128
+ "ui.upload": "upload",
129
+ "ui.avatar": "user",
130
+ "ui.theme-light": "sun",
131
+ "ui.theme-dark": "moon",
132
+ "ui.theme-system": "monitor",
133
+ "ui.trend-up": "arrow-up-right",
134
+ "ui.trend-down": "arrow-down-right",
135
+ "ui.trend-flat": "minus",
136
+ };
137
+
138
+ /** The roles that point along the text: mirrored in a right-to-left context. */
139
+ const MIRRORED = new Set(["ui.disclosure", "ui.previous", "ui.next", "ui.first", "ui.last", "ui.separator"]);
140
+
141
+ /** Whether an icon is drawn mirrored right to left (a role that points along the text). */
142
+ export function mirrorsInRtl(name) {
143
+ return MIRRORED.has(name);
144
+ }
145
+
146
+ const registry = new Map([...Object.entries(DEFAULTS), ...Object.entries(FILLED).map(([name, markup]) => [name + FILLED_SUFFIX, markup])]);
66
147
  const listeners = new Set();
67
148
  const warned = new Set();
68
149
 
69
150
  /**
70
- * Replaces or adds icons. Values are SVG markup or an SVGElement.
151
+ * Replaces or adds icons. Values are SVG markup or an SVGElement; a name
152
+ * ending in ":filled" is the filled version of an icon.
71
153
  * @param {Record<string, string | SVGElement>} icons
72
154
  */
73
155
  export function setIcons(icons) {
@@ -81,9 +163,19 @@ export function setIcons(icons) {
81
163
  for (const cb of listeners) cb();
82
164
  }
83
165
 
84
- /** The SVG markup of an icon ("" and one warning when it is not registered). */
85
- export function getIcon(name) {
86
- const markup = registry.get(name);
166
+ /**
167
+ * The SVG markup of an icon or a role ("" and one warning when it is not
168
+ * registered). With `filled`, its filled version, or the outline one when it
169
+ * has none.
170
+ */
171
+ export function getIcon(name, { filled = false } = {}) {
172
+ // A role the app has not registered draws its glyph.
173
+ const glyph = ALIASES[name];
174
+ if (filled) {
175
+ const solidMarkup = registry.get(name + FILLED_SUFFIX) ?? (glyph && !registry.has(name) ? registry.get(glyph + FILLED_SUFFIX) : undefined);
176
+ if (solidMarkup !== undefined) return solidMarkup;
177
+ }
178
+ const markup = registry.get(name) ?? (glyph ? registry.get(glyph) : undefined);
87
179
  if (markup !== undefined) return markup;
88
180
  if (!warned.has(name)) {
89
181
  warned.add(name);
@@ -92,9 +184,16 @@ export function getIcon(name) {
92
184
  return "";
93
185
  }
94
186
 
95
- /** The registered icon names. */
96
- export function iconNames() {
97
- return [...registry.keys()];
187
+ /** The roles the components draw ("ui.close"…) and the glyph each one uses unless registered. */
188
+ export function iconAliases() {
189
+ return { ...ALIASES };
190
+ }
191
+
192
+ /** The registered icon names; with `filled`, those that have a filled version. */
193
+ export function iconNames({ filled = false } = {}) {
194
+ const names = [...registry.keys()];
195
+ if (filled) return names.filter((n) => n.endsWith(FILLED_SUFFIX)).map((n) => n.slice(0, -FILLED_SUFFIX.length));
196
+ return names.filter((n) => !n.endsWith(FILLED_SUFFIX));
98
197
  }
99
198
 
100
199
  /** @internal Called by the core runtime to repaint components after setIcons. */
package/core/position.js CHANGED
@@ -5,12 +5,19 @@
5
5
  // side when it does not fit, and shifted to stay inside the viewport.
6
6
  // Done in JS rather than CSS anchor positioning, which not every webview
7
7
  // ships yet (WebKitGTK, older WKWebView).
8
+ //
9
+ // The sides top/bottom/left/right are sides of the screen; the alignments
10
+ // -start/-end follow the writing direction of the floating element: right to
11
+ // left, "bottom-start" lines up the right edges.
12
+
13
+ import { isRtl } from "./direction.js";
8
14
 
9
15
  const OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" };
10
16
 
11
17
  /**
12
18
  * @typedef {object} PlaceOptions
13
- * @property {string} [placement] side[-align]: top|bottom|left|right, -start|-end (default "bottom")
19
+ * @property {string} [placement] side[-align]: top|bottom|left|right, -start|-end (default "bottom");
20
+ * on top/bottom, -start/-end follow the writing direction
14
21
  * @property {number} [offset] gap to the anchor in px (default 8)
15
22
  * @property {number} [padding] minimum distance to the viewport edges (default 8)
16
23
  * @property {boolean} [flip] try the opposite side when it does not fit (default true)
@@ -45,8 +52,9 @@ export function place(anchor, floating, { placement = "bottom", offset = 8, padd
45
52
  let y;
46
53
  if (side === "top" || side === "bottom") {
47
54
  y = side === "bottom" ? anchor.bottom + offset : anchor.top - offset - f.height;
48
- x = align === "start" ? anchor.left
49
- : align === "end" ? anchor.right - f.width
55
+ const rtl = align !== "center" && isRtl(floating);
56
+ x = align === (rtl ? "end" : "start") ? anchor.left
57
+ : align === (rtl ? "start" : "end") ? anchor.right - f.width
50
58
  : anchor.left + anchor.width / 2 - f.width / 2;
51
59
  } else {
52
60
  x = side === "right" ? anchor.right + offset : anchor.left - offset - f.width;
package/core/styles.js CHANGED
@@ -154,7 +154,7 @@ export const loaderStyles = css`
154
154
  width: var(--_loader, 1em);
155
155
  height: var(--_loader, 1em);
156
156
  border: max(2px, calc(var(--_loader, 1em) / 8)) solid currentColor;
157
- border-right-color: transparent;
157
+ border-right-color: transparent; /* physical */
158
158
  border-radius: 50%;
159
159
  animation: ray-spin 0.75s linear infinite;
160
160
  }
package/core.js CHANGED
@@ -8,6 +8,7 @@ export { COLORS, VARIANT_TOKENS, applyColor, spaceValue, sizeStyles, radiusStyle
8
8
  export { place, pointRect, autoUpdate } from "./core/position.js";
9
9
  export { scrollerOf, watchScroll } from "./core/scroll.js";
10
10
  export { tabbables, focusFirst } from "./core/focus.js";
11
+ export { isRtl, inlineKey } from "./core/direction.js";
11
12
  export { duration, motionAllowed, play, playAll, collapseOut, POP_OUT } from "./core/motion.js";
12
13
  export { dismissable } from "./core/dismiss.js";
13
14
  export { matchesHotkey, formatHotkey } from "./core/hotkey.js";