proscenium 0.19.0.beta5-arm64-darwin → 0.19.0.beta7-arm64-darwin

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -26
  3. data/lib/proscenium/builder.rb +11 -35
  4. data/lib/proscenium/bundled_gems.rb +37 -0
  5. data/lib/proscenium/css_module/transformer.rb +1 -1
  6. data/lib/proscenium/ext/proscenium +0 -0
  7. data/lib/proscenium/ext/proscenium.h +1 -7
  8. data/lib/proscenium/helper.rb +3 -9
  9. data/lib/proscenium/importer.rb +13 -11
  10. data/lib/proscenium/log_subscriber.rb +0 -12
  11. data/lib/proscenium/middleware/base.rb +11 -4
  12. data/lib/proscenium/middleware/esbuild.rb +1 -6
  13. data/lib/proscenium/middleware/ruby_gems.rb +23 -0
  14. data/lib/proscenium/middleware.rb +26 -22
  15. data/lib/proscenium/monkey.rb +3 -5
  16. data/lib/proscenium/phlex/asset_inclusions.rb +0 -1
  17. data/lib/proscenium/railtie.rb +2 -22
  18. data/lib/proscenium/registry/bundled_package.rb +29 -0
  19. data/lib/proscenium/registry/package.rb +95 -0
  20. data/lib/proscenium/registry/ruby_gem_package.rb +28 -0
  21. data/lib/proscenium/registry.rb +29 -0
  22. data/lib/proscenium/resolver.rb +23 -18
  23. data/lib/proscenium/ruby_gems.rb +67 -0
  24. data/lib/proscenium/side_load.rb +20 -63
  25. data/lib/proscenium/ui/flash/bun.lock +19 -0
  26. data/lib/proscenium/ui/flash/index.js +6 -2
  27. data/lib/proscenium/ui/flash/node_modules/dom-mutations/index.d.ts +33 -0
  28. data/lib/proscenium/ui/flash/node_modules/dom-mutations/index.js +44 -0
  29. data/lib/proscenium/ui/flash/node_modules/dom-mutations/license +9 -0
  30. data/lib/proscenium/ui/flash/node_modules/dom-mutations/package.json +59 -0
  31. data/lib/proscenium/ui/flash/node_modules/dom-mutations/readme.md +125 -0
  32. data/lib/proscenium/ui/flash/node_modules/sourdough-toast/LICENSE +20 -0
  33. data/lib/proscenium/ui/flash/node_modules/sourdough-toast/README.md +11 -0
  34. data/lib/proscenium/ui/flash/node_modules/sourdough-toast/package.json +44 -0
  35. data/lib/proscenium/ui/flash/node_modules/sourdough-toast/src/sourdough-toast.css +697 -0
  36. data/lib/proscenium/ui/flash/node_modules/sourdough-toast/src/sourdough-toast.js +537 -0
  37. data/lib/proscenium/ui/flash/package.json +11 -0
  38. data/lib/proscenium/ui/form/fields/base.rb +1 -1
  39. data/lib/proscenium/ui/form/fields/select.rb +1 -1
  40. data/lib/proscenium/ui/react-manager/index.jsx +3 -22
  41. data/lib/proscenium/ui/ujs/index.js +1 -1
  42. data/lib/proscenium/version.rb +1 -1
  43. data/lib/proscenium.rb +3 -4
  44. metadata +21 -3
  45. data/lib/proscenium/middleware/engines.rb +0 -41
@@ -0,0 +1,537 @@
1
+ // vim: foldmethod=marker
2
+
3
+ const DEFAULT_OPTIONS = {
4
+ maxToasts: 3,
5
+ duration: 4000,
6
+ width: 356,
7
+ gap: 14,
8
+ theme: "light",
9
+ viewportOffset: 32,
10
+ expandedByDefault: false,
11
+ yPosition: "bottom",
12
+ xPosition: "right",
13
+ };
14
+
15
+ let toastsCounter = 0;
16
+
17
+ // {{{ Helpers
18
+ const SVG_NS = "http://www.w3.org/2000/svg";
19
+
20
+ const svgTags = ["svg", "path", "line", "circle"];
21
+
22
+ const h = (tag, props = {}, children = [], isSvg = false) => {
23
+ const element =
24
+ svgTags.includes(tag) || isSvg
25
+ ? document.createElementNS(SVG_NS, tag)
26
+ : document.createElement(tag);
27
+
28
+ for (const [key, value] of Object.entries(props)) {
29
+ if (key === "style") {
30
+ if (typeof value === "string") {
31
+ element.style = value;
32
+ } else {
33
+ for (const [k, v] of Object.entries(value)) {
34
+ element.style.setProperty(k, v);
35
+ }
36
+ }
37
+ } else if (key === "dataset") {
38
+ for (const [k, v] of Object.entries(value)) {
39
+ element.dataset[k] = v;
40
+ }
41
+ } else {
42
+ element.setAttribute(key, value);
43
+ }
44
+ }
45
+
46
+ for (const child of children) {
47
+ if (typeof child === "string") {
48
+ element.appendChild(document.createTextNode(child));
49
+ continue;
50
+ } else if (Array.isArray(child)) {
51
+ for (const c of child) {
52
+ element.appendChild(c);
53
+ }
54
+ continue;
55
+ } else if (child instanceof HTMLElement || child instanceof SVGElement) {
56
+ element.appendChild(child);
57
+ }
58
+ }
59
+
60
+ return element;
61
+ };
62
+
63
+ const svgAttrs = {
64
+ xmlns: "http://www.w3.org/2000/svg",
65
+ viewBox: "0 0 24 24",
66
+ height: "20",
67
+ width: "20",
68
+ fill: "none",
69
+ stroke: "currentColor",
70
+ "stroke-width": "1.5",
71
+ "stroke-linecap": "round",
72
+ "stroke-linejoin": "round",
73
+ dataset: { slot: "icon" },
74
+ };
75
+
76
+ const icons = {
77
+ success: h("svg", svgAttrs, [h("path", { d: "M20 6 9 17l-5-5" })]),
78
+ info: h("svg", svgAttrs, [
79
+ h("circle", { cx: "12", cy: "12", r: "10" }),
80
+ h("path", { d: "M12 16v-4" }),
81
+ h("path", { d: "M12 8h.01" }),
82
+ ]),
83
+ warning: h("svg", svgAttrs, [
84
+ h("path", {
85
+ d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",
86
+ }),
87
+ h("path", { d: "M12 9v4" }),
88
+ h("path", { d: "M12 17h.01" }),
89
+ ]),
90
+ error: h("svg", svgAttrs, [
91
+ h("circle", { cx: "12", cy: "12", r: "10" }),
92
+ h("path", { d: "m4.9 4.9 14.2 14.2" }),
93
+ ]),
94
+ spinner: h(
95
+ "svg",
96
+ {
97
+ ...svgAttrs,
98
+ viewBox: "0 0 2400 2400",
99
+ stroke: "black",
100
+ "data-sourdough-spinner": "",
101
+ "stroke-width": "200",
102
+ "stroke-linecap": "round",
103
+ },
104
+ [
105
+ h("line", { x1: "1200", y1: "600", x2: "1200", y2: "100" }),
106
+ h("line", {
107
+ opacity: "0.5",
108
+ x1: "1200",
109
+ y1: "2300",
110
+ x2: "1200",
111
+ y2: "1800",
112
+ }),
113
+ h("line", {
114
+ opacity: "0.917",
115
+ x1: "900",
116
+ y1: "680.4",
117
+ x2: "650",
118
+ y2: "247.4",
119
+ }),
120
+ h("line", {
121
+ opacity: "0.417",
122
+ x1: "1750",
123
+ y1: "2152.6",
124
+ x2: "1500",
125
+ y2: "1719.6",
126
+ }),
127
+ h("line", {
128
+ opacity: "0.833",
129
+ x1: "680.4",
130
+ y1: "900",
131
+ x2: "247.4",
132
+ y2: "650",
133
+ }),
134
+ h("line", {
135
+ opacity: "0.333",
136
+ x1: "2152.6",
137
+ y1: "1750",
138
+ x2: "1719.6",
139
+ y2: "1500",
140
+ }),
141
+ h("line", {
142
+ opacity: "0.75",
143
+ x1: "600",
144
+ y1: "1200",
145
+ x2: "100",
146
+ y2: "1200",
147
+ }),
148
+ h("line", {
149
+ opacity: "0.25",
150
+ x1: "2300",
151
+ y1: "1200",
152
+ x2: "1800",
153
+ y2: "1200",
154
+ }),
155
+ h("line", {
156
+ opacity: "0.667",
157
+ x1: "680.4",
158
+ y1: "1500",
159
+ x2: "247.4",
160
+ y2: "1750",
161
+ }),
162
+ h("line", {
163
+ opacity: "0.167",
164
+ x1: "2152.6",
165
+ y1: "650",
166
+ x2: "1719.6",
167
+ y2: "900",
168
+ }),
169
+ h("line", {
170
+ opacity: "0.583",
171
+ x1: "900",
172
+ y1: "1719.6",
173
+ x2: "650",
174
+ y2: "2152.6",
175
+ }),
176
+ h("line", {
177
+ opacity: "0.083",
178
+ x1: "1750",
179
+ y1: "247.4",
180
+ x2: "1500",
181
+ y2: "680.4",
182
+ }),
183
+ ],
184
+ ),
185
+ };
186
+
187
+ function getDocumentDirection() {
188
+ if (typeof window === "undefined") return "ltr";
189
+
190
+ const dirAttribute = document.documentElement.getAttribute("dir");
191
+
192
+ if (dirAttribute === "auto" || !dirAttribute) {
193
+ return window.getComputedStyle(document.documentElement).direction;
194
+ }
195
+
196
+ return dirAttribute;
197
+ }
198
+ // }}}
199
+
200
+ // {{{ class Store
201
+ class Store extends EventTarget {
202
+ #subscribers = [];
203
+
204
+ constructor() {
205
+ super();
206
+
207
+ this.data = {
208
+ toasts: [],
209
+ expanded: false,
210
+ interacting: false,
211
+ };
212
+ }
213
+
214
+ subscribe = (fn) => {
215
+ this.#subscribers.push(fn);
216
+
217
+ return () => {
218
+ this.#subscribers = this.#subscribers.filter((f) => f !== fn);
219
+ };
220
+ };
221
+
222
+ #publish = () => {
223
+ this.#subscribers.forEach((fn) => fn(this.data));
224
+ };
225
+
226
+ #set = (fn) => {
227
+ const change = fn({ ...this.data });
228
+ this.data = { ...this.data, ...change };
229
+ this.#publish(this.data);
230
+ };
231
+
232
+ touch = () => {
233
+ this.#publish();
234
+ };
235
+
236
+ create = (opts) => {
237
+ const id = (toastsCounter++).toString();
238
+ const toast = { id, ...opts };
239
+
240
+ this.#set((data) => ({
241
+ toasts: [...data.toasts, toast],
242
+ }));
243
+ };
244
+
245
+ remove = (id) => {
246
+ this.#set((data) => ({
247
+ toasts: data.toasts.filter((t) => t.id !== id),
248
+ }));
249
+ };
250
+
251
+ expand = () => {
252
+ this.#set(() => ({ expanded: true }));
253
+ };
254
+
255
+ collapse = () => {
256
+ this.#set(() => ({ expanded: false }));
257
+ };
258
+
259
+ focus = () => {
260
+ this.#set(() => ({ interacting: true }));
261
+ };
262
+
263
+ blur = () => {
264
+ this.#set(() => ({ interacting: false }));
265
+ };
266
+ } // }}}
267
+
268
+ // {{{ class Toast
269
+ class Toast {
270
+ constructor(sourdough, opts = {}) {
271
+ this.sourdough = sourdough;
272
+ this.opts = opts;
273
+
274
+ this.paused = false;
275
+
276
+ const children = [];
277
+
278
+ let icon = null;
279
+ if (opts.type) {
280
+ switch (opts.type) {
281
+ case "success":
282
+ icon = icons.success;
283
+ break;
284
+ case "info":
285
+ icon = icons.info;
286
+ break;
287
+ case "warning":
288
+ icon = icons.warning;
289
+ break;
290
+ case "error":
291
+ icon = icons.error;
292
+ break;
293
+ }
294
+ }
295
+
296
+ if (icon) {
297
+ children.push(
298
+ h("div", { dataset: { icon: "" } }, [icon.cloneNode(true)]),
299
+ );
300
+ }
301
+
302
+ const contentChildren = [];
303
+
304
+ const title = h("div", { dataset: { title: "" } }, opts.title);
305
+
306
+ contentChildren.push(title);
307
+
308
+ if (opts.description) {
309
+ const description = h(
310
+ "div",
311
+ { dataset: { description: "" } },
312
+ opts.description,
313
+ );
314
+ contentChildren.push(description);
315
+ }
316
+
317
+ const content = h("div", { dataset: { content: "" } }, contentChildren);
318
+
319
+ children.push(content);
320
+
321
+ const li = h(
322
+ "li",
323
+ {
324
+ dataset: {
325
+ sourdoughToast: "",
326
+ expanded: sourdough.opts.expanded,
327
+ styled: true,
328
+ swiping: false,
329
+ swipeOut: false,
330
+ type: opts.type,
331
+ yPosition: sourdough.opts.yPosition,
332
+ xPosition: sourdough.opts.xPosition,
333
+ },
334
+ style: {},
335
+ },
336
+ [children],
337
+ );
338
+
339
+ this.element = li;
340
+ }
341
+
342
+ mount = () => {
343
+ this.element.dataset.mounted = "true";
344
+
345
+ this.initialHeight = this.element.offsetHeight;
346
+
347
+ state.touch();
348
+
349
+ this.timeLeft = this.sourdough.opts.duration;
350
+ this.resume();
351
+ };
352
+
353
+ remove = () => {
354
+ this.element.dataset.removed = "true";
355
+
356
+ setTimeout(() => {
357
+ this.element.remove();
358
+ state.remove(this.opts.id);
359
+ }, 400);
360
+ };
361
+
362
+ pause = () => {
363
+ this.paused = true;
364
+ this.timeLeft = this.timeLeft - (Date.now() - this.startedAt);
365
+ clearTimeout(this.timer);
366
+ };
367
+
368
+ resume = () => {
369
+ this.paused = false;
370
+ this.startedAt = Date.now();
371
+ this.timer = setTimeout(this.remove, this.timeLeft);
372
+ };
373
+ }
374
+ // }}}
375
+
376
+ class Sourdough {
377
+ constructor(opts = {}) {
378
+ this.opts = Object.assign({}, DEFAULT_OPTIONS, opts);
379
+
380
+ this.expanded = this.opts.expandedByDefault;
381
+ if (this.opts.expandedByDefault) setTimeout(state.expand);
382
+
383
+ // Cache rendered toasts by id
384
+ this.renderedToastsById = {};
385
+
386
+ this.list = h("ol", {
387
+ dir: getDocumentDirection(),
388
+ dataset: {
389
+ sourdoughToaster: "",
390
+ expanded: this.expanded,
391
+ theme: this.opts.theme,
392
+ richColors: this.opts.richColors,
393
+ yPosition: this.opts.yPosition,
394
+ xPosition: this.opts.xPosition,
395
+ },
396
+ style: {
397
+ "--width": `${this.opts.width}px`,
398
+ "--gap": `${this.opts.gap}px`,
399
+ "--offset": `${this.opts.viewportOffset}px`,
400
+ },
401
+ });
402
+
403
+ this.list.addEventListener("mouseenter", state.focus);
404
+ this.list.addEventListener("mouseleave", state.blur);
405
+
406
+ this.element = h(
407
+ "div",
408
+ {
409
+ dataset: {
410
+ sourdough: "",
411
+ ...opts.dataset,
412
+ },
413
+ },
414
+ [this.list],
415
+ );
416
+
417
+ this.subscription = state.subscribe(this.update.bind(this));
418
+ }
419
+
420
+ boot = () => {
421
+ if (document.querySelector("[data-sourdough]")) {
422
+ return;
423
+ }
424
+ document.body.appendChild(this.element);
425
+ };
426
+
427
+ update = (state) => {
428
+ this.expanded = state.expanded || state.interacting;
429
+ this.list.dataset.expanded = this.expanded;
430
+
431
+ // Get first X toasts
432
+ const toasts = state.toasts.slice(-this.opts.maxToasts);
433
+
434
+ // Render and cache toasts that haven't been rendered yet
435
+ const renderedIds = [];
436
+ const toastsToRender = toasts.reduce((coll, t) => {
437
+ renderedIds.push(t.id);
438
+ coll.push(this.renderedToastsById[t.id] || this.createToast(t));
439
+ return coll;
440
+ }, []);
441
+
442
+ // Uncache and remove toast elements that are not to be rendered
443
+ Object.keys(this.renderedToastsById).forEach((id) => {
444
+ if (!renderedIds.includes(id)) {
445
+ this.renderedToastsById[id].element.remove();
446
+ delete this.renderedToastsById[id];
447
+ }
448
+ });
449
+
450
+ const front = toastsToRender[toastsToRender.length - 1];
451
+
452
+ if (front) {
453
+ this.list.style.setProperty(
454
+ "--front-toast-height",
455
+ `${front.element.offsetHeight}px`,
456
+ );
457
+ }
458
+
459
+ for (const [index, t] of toastsToRender.entries()) {
460
+ if (t.paused && !state.interacting) {
461
+ t.resume();
462
+ } else if (!t.paused && state.interacting) {
463
+ t.pause();
464
+ }
465
+
466
+ t.element.dataset.index = index;
467
+ t.element.dataset.front = t === front;
468
+ t.element.dataset.expanded = this.expanded;
469
+
470
+ t.element.style.setProperty("--index", index);
471
+ t.element.style.setProperty(
472
+ "--toasts-before",
473
+ toastsToRender.length - index - 1,
474
+ );
475
+ t.element.style.setProperty("--z-index", index);
476
+
477
+ t.element.style.setProperty(
478
+ "--initial-height",
479
+ this.expanded ? "auto" : `${t.initialHeight}px`,
480
+ );
481
+
482
+ // Calculate offset by adding all the heights of the toasts before
483
+ // the current one + the gap between them.
484
+ // Note: We're calculating the total height once per loop which is
485
+ // not ideal.
486
+ const [heightBefore, totalHeight] = toastsToRender.reduce(
487
+ ([before, total], t, i) => {
488
+ const boxHeight = t.initialHeight + this.opts.gap;
489
+ if (i < index) before += boxHeight;
490
+ total += boxHeight;
491
+ return [before, total];
492
+ },
493
+ [0, 0],
494
+ );
495
+
496
+ const offset =
497
+ totalHeight - heightBefore - t.initialHeight - this.opts.gap;
498
+ t.element.style.setProperty(
499
+ "--offset",
500
+ `${t.element.dataset.removed ? "0" : offset || 0}px`,
501
+ );
502
+ }
503
+ };
504
+
505
+ createToast = (opts) => {
506
+ const toast = new Toast(this, opts);
507
+ this.renderedToastsById[opts.id] = toast;
508
+ this.list.appendChild(toast.element);
509
+
510
+ setTimeout(toast.mount, 0);
511
+
512
+ return toast;
513
+ };
514
+ }
515
+
516
+ const state = new Store();
517
+
518
+ const toast = (title) => {
519
+ state.create({ title });
520
+ };
521
+ toast.message = ({ title, description, ...opts }) => {
522
+ state.create({ title, description, ...opts });
523
+ };
524
+ toast.success = (title, opts = {}) => {
525
+ state.create({ title, type: "success", ...opts });
526
+ };
527
+ toast.info = (title, opts = {}) => {
528
+ state.create({ title, type: "info", ...opts });
529
+ };
530
+ toast.warning = (title, opts = {}) => {
531
+ state.create({ title, type: "warning", ...opts });
532
+ };
533
+ toast.error = (title, opts = {}) => {
534
+ state.create({ title, type: "error", ...opts });
535
+ };
536
+
537
+ export { toast, Sourdough };
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@proscenium/flash",
3
+ "module": "index.js",
4
+ "type": "module",
5
+ "devDependencies": {
6
+ "sourdough-toast": "latest"
7
+ },
8
+ "dependencies": {
9
+ "dom-mutations": "^1.0.0"
10
+ }
11
+ }
@@ -84,7 +84,7 @@ module Proscenium::UI
84
84
  def label(**kwargs, &block)
85
85
  content = attributes.delete(:label)
86
86
 
87
- super(**kwargs) do
87
+ super do
88
88
  captured = capture do
89
89
  div do
90
90
  span { content || translate_label } if content != false
@@ -115,7 +115,7 @@ module Proscenium::UI::Form::Fields
115
115
  return value
116
116
  end
117
117
 
118
- options.filter { |k, _v| value.include?(k) }.values
118
+ options.slice(*value).values
119
119
  end
120
120
 
121
121
  def options
@@ -1,15 +1,5 @@
1
- window.Proscenium = window.Proscenium || { lazyScripts: {} };
2
1
  const pathAttribute = "data-proscenium-component-path";
3
2
 
4
- // Find lazyscripts JSON already in the DOM.
5
- const element = document.querySelector("#prosceniumLazyScripts");
6
- if (element) {
7
- window.Proscenium.lazyScripts = {
8
- ...window.Proscenium.lazyScripts,
9
- ...JSON.parse(element.text),
10
- };
11
- }
12
-
13
3
  // Find components already in the DOM.
14
4
  const elements = document.querySelectorAll(`[${pathAttribute}]`);
15
5
  elements.length > 0 && init(elements);
@@ -17,12 +7,7 @@ elements.length > 0 && init(elements);
17
7
  new MutationObserver((mutationsList) => {
18
8
  for (const { addedNodes } of mutationsList) {
19
9
  for (const ele of addedNodes) {
20
- if (ele.tagName === "SCRIPT" && ele.id === "prosceniumLazyScripts") {
21
- window.Proscenium.lazyScripts = {
22
- ...window.Proscenium.lazyScripts,
23
- ...JSON.parse(ele.text),
24
- };
25
- } else if (ele.matches(`[${pathAttribute}]`)) {
10
+ if (ele.matches(`[${pathAttribute}]`)) {
26
11
  init([ele]);
27
12
  }
28
13
  }
@@ -81,12 +66,8 @@ function init(elements) {
81
66
  // For testing and simulation of slow connections.
82
67
  // const sim = new Promise((resolve) => setTimeout(resolve, 5000));
83
68
 
84
- if (!window.Proscenium.lazyScripts[path]) {
85
- throw `[proscenium/react/manager] Cannot load component ${path} (not found in Proscenium.lazyScripts)`;
86
- }
87
-
88
- const react = import("proscenium/react-manager/react");
89
- const Component = import(window.Proscenium.lazyScripts[path].outpath);
69
+ const react = import("@rubygems/proscenium/react-manager/react");
70
+ const Component = import(path);
90
71
 
91
72
  const forwardChildren =
92
73
  "prosceniumComponentForwardChildren" in element.dataset &&
@@ -2,7 +2,7 @@ export default async () => {
2
2
  window.Proscenium = window.Proscenium || {};
3
3
 
4
4
  if (!window.Proscenium.UJS) {
5
- const classPath = "/proscenium/ujs/class.js";
5
+ const classPath = "/node_modules/@rubygems/proscenium/ujs/class.js";
6
6
  const module = await import(classPath);
7
7
  window.Proscenium.UJS = new module.default();
8
8
  }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Proscenium
4
- VERSION = '0.19.0.beta5'
4
+ VERSION = '0.19.0.beta7'
5
5
  end
data/lib/proscenium.rb CHANGED
@@ -40,6 +40,9 @@ module Proscenium
40
40
  autoload :Builder
41
41
  autoload :Importer
42
42
  autoload :Resolver
43
+ autoload :BundledGems
44
+ autoload :RubyGems
45
+ autoload :Registry
43
46
  autoload :UI
44
47
 
45
48
  class Deprecator
@@ -66,10 +69,6 @@ module Proscenium
66
69
  @config ||= Railtie.config.proscenium
67
70
  end
68
71
 
69
- def cache
70
- @cache ||= config.cache || ActiveSupport::Cache::NullStore.new
71
- end
72
-
73
72
  def ui_path
74
73
  @ui_path ||= Railtie.root.join('lib', 'proscenium', 'ui')
75
74
  end