tutuca 0.9.97 → 0.9.99

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.
@@ -1,5 +1,48 @@
1
- // src/storybook.js
2
- import { component, dispatchPhase, html, injectCss, tutuca } from "tutuca";
1
+ // src/storybook/index.js
2
+ import { component, dispatchPhase, html, injectCss, phaseHasBubble, tutuca } from "tutuca";
3
+ import { getComponents as getInspectorComponents } from "tutuca/components";
4
+
5
+ // src/storybook/inspect.js
6
+ import { buildInspectorViews, isComponentInstance } from "tutuca/components";
7
+ function buildTestIndex(modules) {
8
+ const index = new Map;
9
+ for (const m of modules) {
10
+ if (typeof m.getTests !== "function")
11
+ continue;
12
+ const components = m.getComponents?.() ?? [];
13
+ for (const c of components) {
14
+ if (!index.has(c.name))
15
+ index.set(c.name, { getTests: m.getTests, components });
16
+ }
17
+ }
18
+ return index;
19
+ }
20
+ async function buildExampleInspectors(example, scope, testIndex, dev) {
21
+ const value = example.value;
22
+ const comp = isComponentInstance(value) ? scope.getCompFor(value) : null;
23
+ const entry = comp ? testIndex.get(comp.name) : null;
24
+ const views = await buildInspectorViews(value, scope, {
25
+ getTests: entry?.getTests ?? null,
26
+ components: entry?.components ?? [],
27
+ dev
28
+ });
29
+ return example.setInstanceView(views.instanceView).setComponentView(views.componentView).setLintView(views.lintView).setTestView(views.testView).setHasInspect(views.hasInspect).setHasComponent(views.hasComponent).setHasLint(views.hasLint).setHasTest(views.hasTest);
30
+ }
31
+ async function attachInspectorViews(root, scope, modules, dev = null) {
32
+ const testIndex = buildTestIndex(modules);
33
+ let sections = root.sections;
34
+ for (let si = 0;si < sections.size; si++) {
35
+ const section = sections.get(si);
36
+ let items = section.items;
37
+ for (let ii = 0;ii < items.size; ii++) {
38
+ items = items.set(ii, await buildExampleInspectors(items.get(ii), scope, testIndex, dev));
39
+ }
40
+ sections = sections.set(si, section.setItems(items));
41
+ }
42
+ return root.setSections(sections);
43
+ }
44
+
45
+ // src/storybook/index.js
3
46
  var Storybook = component({
4
47
  name: "Storybook",
5
48
  fields: {
@@ -247,7 +290,16 @@ var Example = component({
247
290
  value: null,
248
291
  view: "main",
249
292
  requestHandlers: null,
250
- on: null
293
+ on: null,
294
+ activeTab: "preview",
295
+ hasInspect: false,
296
+ hasComponent: false,
297
+ hasLint: false,
298
+ hasTest: false,
299
+ componentView: null,
300
+ instanceView: null,
301
+ lintView: null,
302
+ testView: null
251
303
  },
252
304
  requestOverridesField: "requestHandlers",
253
305
  statics: {
@@ -274,18 +326,25 @@ var Example = component({
274
326
  },
275
327
  receive: {
276
328
  init(ctx) {
277
- dispatchPhase(ctx, ctx.at.field("value").buildPath(), this.on?.init, this.value);
329
+ this.runPhase(ctx, "init", this.on?.init);
278
330
  return this;
279
331
  },
280
332
  resume(ctx) {
281
- dispatchPhase(ctx, ctx.at.field("value").buildPath(), this.on?.resume, this.value);
333
+ this.runPhase(ctx, "resume", this.on?.resume);
282
334
  return this;
283
335
  },
284
336
  suspend(ctx) {
285
- dispatchPhase(ctx, ctx.at.field("value").buildPath(), this.on?.suspend, this.value);
337
+ this.runPhase(ctx, "suspend", this.on?.suspend);
286
338
  return this;
287
339
  }
288
340
  },
341
+ methods: {
342
+ runPhase(ctx, name, phase) {
343
+ if (phaseHasBubble(phase))
344
+ console.warn(`storybook on.${name}: a \`bubble\` action leaves this example and is received by the storybook engine, so your component's bubble handler won't run. Use send/request/input to drive a preset state.`);
345
+ dispatchPhase(ctx, ctx.at.field("value").buildPath(), phase, this.value);
346
+ }
347
+ },
289
348
  input: {
290
349
  onLogSelected() {
291
350
  console.log(this.value);
@@ -310,8 +369,72 @@ var Example = component({
310
369
  </div>
311
370
  </h2>
312
371
  <p class="text-md italic opacity-60" @text=".description"></p>
313
- <div class="bg-base-100 p-3" @push-view=".view">
314
- <x render=".value"></x>
372
+ <div role="tablist" class="tabs tabs-border" @show=".hasInspect">
373
+ <a
374
+ role="tab"
375
+ @if.class="equals? .activeTab 'preview'"
376
+ @then="'tab tab-active'"
377
+ @else="'tab'"
378
+ @on.click="$setActiveTab 'preview'"
379
+ >
380
+ Preview
381
+ </a>
382
+ <a
383
+ role="tab"
384
+ @show=".hasComponent"
385
+ @if.class="equals? .activeTab 'component'"
386
+ @then="'tab tab-active'"
387
+ @else="'tab'"
388
+ @on.click="$setActiveTab 'component'"
389
+ >
390
+ Component
391
+ </a>
392
+ <a
393
+ role="tab"
394
+ @if.class="equals? .activeTab 'instance'"
395
+ @then="'tab tab-active'"
396
+ @else="'tab'"
397
+ @on.click="$setActiveTab 'instance'"
398
+ >
399
+ Instance
400
+ </a>
401
+ <a
402
+ role="tab"
403
+ @show=".hasLint"
404
+ @if.class="equals? .activeTab 'lint'"
405
+ @then="'tab tab-active'"
406
+ @else="'tab'"
407
+ @on.click="$setActiveTab 'lint'"
408
+ >
409
+ Lint
410
+ </a>
411
+ <a
412
+ role="tab"
413
+ @show=".hasTest"
414
+ @if.class="equals? .activeTab 'test'"
415
+ @then="'tab tab-active'"
416
+ @else="'tab'"
417
+ @on.click="$setActiveTab 'test'"
418
+ >
419
+ Test
420
+ </a>
421
+ </div>
422
+ <div @show="equals? .activeTab 'preview'">
423
+ <div class="bg-base-100 p-3" @push-view=".view">
424
+ <x render=".value"></x>
425
+ </div>
426
+ </div>
427
+ <div class="p-3" @show="equals? .activeTab 'component'">
428
+ <x render=".componentView"></x>
429
+ </div>
430
+ <div class="p-3" @show="equals? .activeTab 'instance'">
431
+ <x render=".instanceView"></x>
432
+ </div>
433
+ <div class="p-3" @show="equals? .activeTab 'lint'">
434
+ <x render=".lintView"></x>
435
+ </div>
436
+ <div class="p-3" @show="equals? .activeTab 'test'">
437
+ <x render=".testView"></x>
315
438
  </div>
316
439
  </div>
317
440
  </div>`
@@ -353,7 +476,7 @@ function buildStorybook(modules) {
353
476
  return Array.isArray(raw) ? raw : [raw];
354
477
  });
355
478
  const sections = rawSections.map((s) => Section.Class.fromData(s)).sort((a, b) => a.title.localeCompare(b.title));
356
- const components = new Set([Storybook, Section, Example]);
479
+ const components = new Set([Storybook, Section, Example, ...getInspectorComponents()]);
357
480
  const macros = {};
358
481
  const requestHandlers = {};
359
482
  const overrideNames = new Set;
@@ -406,7 +529,7 @@ function buildExampleRequestHandlers({ requestHandlers: reals, overrideNames })
406
529
  handlers[name] = makeMeta(name);
407
530
  return handlers;
408
531
  }
409
- async function mountStorybook(selector, modules, { compileCss, root, persistUrl = true } = {}) {
532
+ async function mountStorybook(selector, modules, { compileCss, root, persistUrl = true, dev = null } = {}) {
410
533
  const app = tutuca(selector);
411
534
  const built = buildStorybook(modules);
412
535
  app.state.set(root ?? built.root);
@@ -417,6 +540,9 @@ async function mountStorybook(selector, modules, { compileCss, root, persistUrl
417
540
  if (persistUrl) {
418
541
  scope.registerRequestHandlers({ persistState });
419
542
  }
543
+ if (dev && app.state.val?.sections) {
544
+ app.state.set(await attachInspectorViews(app.state.val, scope, modules, dev));
545
+ }
420
546
  if (compileCss) {
421
547
  injectCss("tutuca-storybook", await compileCss(app));
422
548
  }
@@ -1744,37 +1744,47 @@ class Renderer {
1744
1744
  renderEach(stack, iterInfo, node, viewName) {
1745
1745
  const { seq, filter, loopWith } = iterInfo.eval(stack);
1746
1746
  const r = [];
1747
- const { iterData, start, end } = unpackLoopResult(loopWith.call(stack.it, seq), seq);
1748
- getSeqInfo(seq)(seq, (key, value, attrName) => {
1749
- if (filter.call(stack.it, key, value, iterData)) {
1750
- const dom = this.renderIt(stack.enter(value, { key }, true), node, key, viewName);
1751
- this.pushEachEntry(r, node.nodeId, attrName, key, dom);
1752
- }
1753
- }, start, end);
1747
+ const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(stack.it, seq, makeLoopCtx(stack, filter)), seq);
1748
+ const renderOne = (key, value, attrName) => {
1749
+ const dom = this.renderIt(stack.enter(value, { key }, true), node, key, viewName);
1750
+ this.pushEachEntry(r, node.nodeId, attrName, key, dom);
1751
+ };
1752
+ if (keys)
1753
+ imKeysIter(seq, renderOne, keys);
1754
+ else
1755
+ getSeqInfo(seq)(seq, (key, value, attrName) => {
1756
+ if (filter.call(stack.it, key, value, iterData))
1757
+ renderOne(key, value, attrName);
1758
+ }, start, end);
1754
1759
  return r;
1755
1760
  }
1756
1761
  renderEachWhen(stack, iterInfo, view, nid) {
1757
1762
  const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
1758
1763
  const r = [];
1759
1764
  const it = stack.it;
1760
- const { iterData, start, end } = unpackLoopResult(loopWith.call(it, seq), seq);
1761
- getSeqInfo(seq)(seq, (key, value, attrName) => {
1762
- if (filter.call(it, key, value, iterData)) {
1763
- const cachePath = enricher ? [view, it, value] : [view, value];
1764
- const binds = { key, value };
1765
- const cacheKey = `${nid}-${key}`;
1766
- if (enricher)
1767
- enricher.call(it, binds, key, value, iterData);
1768
- const cachedNode = this.cache.get(cachePath, cacheKey);
1769
- if (cachedNode)
1770
- this.pushEachEntry(r, nid, attrName, key, cachedNode);
1771
- else {
1772
- const dom = this.renderView(view, stack.enter(value, binds, false));
1773
- this.pushEachEntry(r, nid, attrName, key, dom);
1774
- this.cache.set(cachePath, cacheKey, dom);
1775
- }
1765
+ const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(it, seq, makeLoopCtx(stack, filter)), seq);
1766
+ const renderOne = (key, value, attrName) => {
1767
+ const cachePath = enricher ? [view, it, value] : [view, value];
1768
+ const binds = { key, value };
1769
+ const cacheKey = `${nid}-${key}`;
1770
+ if (enricher)
1771
+ enricher.call(it, binds, key, value, iterData);
1772
+ const cachedNode = this.cache.get(cachePath, cacheKey);
1773
+ if (cachedNode)
1774
+ this.pushEachEntry(r, nid, attrName, key, cachedNode);
1775
+ else {
1776
+ const dom = this.renderView(view, stack.enter(value, binds, false));
1777
+ this.pushEachEntry(r, nid, attrName, key, dom);
1778
+ this.cache.set(cachePath, cacheKey, dom);
1776
1779
  }
1777
- }, start, end);
1780
+ };
1781
+ if (keys)
1782
+ imKeysIter(seq, renderOne, keys);
1783
+ else
1784
+ getSeqInfo(seq)(seq, (key, value, attrName) => {
1785
+ if (filter.call(it, key, value, iterData))
1786
+ renderOne(key, value, attrName);
1787
+ }, start, end);
1778
1788
  return r;
1779
1789
  }
1780
1790
  renderView(view, stack) {
@@ -1810,8 +1820,17 @@ var filterAlwaysTrue = (_v, _k, _seq) => true;
1810
1820
  var nullLoopWith = (seq) => ({ iterData: { seq } });
1811
1821
  var unpackLoopResult = (result, seq) => {
1812
1822
  const r = result ?? {};
1813
- return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end };
1823
+ return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end, keys: r.keys };
1824
+ };
1825
+ var imKeysIter = (seq, visit, keys) => {
1826
+ const attrName = isIndexed(seq) ? "si" : "sk";
1827
+ for (const key of keys)
1828
+ visit(key, seq.get(key), attrName);
1814
1829
  };
1830
+ var makeLoopCtx = (stack, filter) => ({
1831
+ lookup: (name) => stack.lookupBind(name),
1832
+ filter: (key, value, iterData) => filter.call(stack.it, key, value, iterData)
1833
+ });
1815
1834
  var imIndexedIter = (seq, visit, start, end) => {
1816
1835
  const [s, e] = normalizeRange(start, end, seq.size);
1817
1836
  for (let i = s;i < e; i++)
@@ -2359,11 +2378,11 @@ class IterInfo {
2359
2378
  return { seq, filter, loopWith, enricher };
2360
2379
  }
2361
2380
  enrichBinds(stack, key) {
2362
- const { seq, loopWith, enricher } = this.eval(stack);
2381
+ const { seq, filter, loopWith, enricher } = this.eval(stack);
2363
2382
  const value = seq?.get ? seq.get(key, null) : null;
2364
2383
  const binds = { key, value };
2365
2384
  if (enricher) {
2366
- const { iterData } = unpackLoopResult(loopWith.call(stack.it, seq), seq);
2385
+ const { iterData } = unpackLoopResult(loopWith.call(stack.it, seq, makeLoopCtx(stack, filter)), seq);
2367
2386
  enricher.call(stack.it, binds, key, value, iterData);
2368
2387
  }
2369
2388
  return binds;
@@ -3714,6 +3733,13 @@ function phaseOps(phase) {
3714
3733
  function resolveArgs(args, self) {
3715
3734
  return typeof args === "function" ? args(self) ?? [] : args ?? [];
3716
3735
  }
3736
+ function phaseHasBubble(phase) {
3737
+ if (!phase)
3738
+ return false;
3739
+ if (phase.bubble?.length)
3740
+ return true;
3741
+ return (phase.do ?? []).some((op) => op.type === "bubble");
3742
+ }
3717
3743
  function dispatchPhase(dispatcher, targetPath, phase, self) {
3718
3744
  if (!phase)
3719
3745
  return;
@@ -4007,7 +4033,8 @@ class FieldSet extends Field {
4007
4033
  }
4008
4034
  function mkCompField(field, scope, args) {
4009
4035
  const Comp = scope?.lookupComponent(field.type) ?? null;
4010
- console.assert(!scope || Comp !== null, "component not found", { field });
4036
+ if (Comp === null)
4037
+ console.warn(scope ? `component field "${field.name}": component "${field.type}" not found in scope` : `component field "${field.name}": cannot resolve component "${field.type}" — built without a registered scope (use ${field.type}.make({}) as the default, or build via a registered component)`);
4011
4038
  return Comp?.make({ ...field.args, ...args }, { scope }) ?? null;
4012
4039
  }
4013
4040
 
@@ -4156,6 +4183,7 @@ export {
4156
4183
  removeIn,
4157
4184
  remove,
4158
4185
  phaseOps,
4186
+ phaseHasBubble,
4159
4187
  mergeWith,
4160
4188
  mergeDeepWith,
4161
4189
  mergeDeep,
package/dist/tutuca.js CHANGED
@@ -6114,37 +6114,47 @@ class Renderer {
6114
6114
  renderEach(stack, iterInfo, node, viewName) {
6115
6115
  const { seq, filter, loopWith } = iterInfo.eval(stack);
6116
6116
  const r = [];
6117
- const { iterData, start, end } = unpackLoopResult(loopWith.call(stack.it, seq), seq);
6118
- getSeqInfo(seq)(seq, (key, value, attrName) => {
6119
- if (filter.call(stack.it, key, value, iterData)) {
6120
- const dom = this.renderIt(stack.enter(value, { key }, true), node, key, viewName);
6121
- this.pushEachEntry(r, node.nodeId, attrName, key, dom);
6122
- }
6123
- }, start, end);
6117
+ const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(stack.it, seq, makeLoopCtx(stack, filter)), seq);
6118
+ const renderOne = (key, value, attrName) => {
6119
+ const dom = this.renderIt(stack.enter(value, { key }, true), node, key, viewName);
6120
+ this.pushEachEntry(r, node.nodeId, attrName, key, dom);
6121
+ };
6122
+ if (keys)
6123
+ imKeysIter(seq, renderOne, keys);
6124
+ else
6125
+ getSeqInfo(seq)(seq, (key, value, attrName) => {
6126
+ if (filter.call(stack.it, key, value, iterData))
6127
+ renderOne(key, value, attrName);
6128
+ }, start, end);
6124
6129
  return r;
6125
6130
  }
6126
6131
  renderEachWhen(stack, iterInfo, view, nid) {
6127
6132
  const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
6128
6133
  const r = [];
6129
6134
  const it = stack.it;
6130
- const { iterData, start, end } = unpackLoopResult(loopWith.call(it, seq), seq);
6131
- getSeqInfo(seq)(seq, (key, value, attrName) => {
6132
- if (filter.call(it, key, value, iterData)) {
6133
- const cachePath = enricher ? [view, it, value] : [view, value];
6134
- const binds = { key, value };
6135
- const cacheKey = `${nid}-${key}`;
6136
- if (enricher)
6137
- enricher.call(it, binds, key, value, iterData);
6138
- const cachedNode = this.cache.get(cachePath, cacheKey);
6139
- if (cachedNode)
6140
- this.pushEachEntry(r, nid, attrName, key, cachedNode);
6141
- else {
6142
- const dom = this.renderView(view, stack.enter(value, binds, false));
6143
- this.pushEachEntry(r, nid, attrName, key, dom);
6144
- this.cache.set(cachePath, cacheKey, dom);
6145
- }
6135
+ const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(it, seq, makeLoopCtx(stack, filter)), seq);
6136
+ const renderOne = (key, value, attrName) => {
6137
+ const cachePath = enricher ? [view, it, value] : [view, value];
6138
+ const binds = { key, value };
6139
+ const cacheKey = `${nid}-${key}`;
6140
+ if (enricher)
6141
+ enricher.call(it, binds, key, value, iterData);
6142
+ const cachedNode = this.cache.get(cachePath, cacheKey);
6143
+ if (cachedNode)
6144
+ this.pushEachEntry(r, nid, attrName, key, cachedNode);
6145
+ else {
6146
+ const dom = this.renderView(view, stack.enter(value, binds, false));
6147
+ this.pushEachEntry(r, nid, attrName, key, dom);
6148
+ this.cache.set(cachePath, cacheKey, dom);
6146
6149
  }
6147
- }, start, end);
6150
+ };
6151
+ if (keys)
6152
+ imKeysIter(seq, renderOne, keys);
6153
+ else
6154
+ getSeqInfo(seq)(seq, (key, value, attrName) => {
6155
+ if (filter.call(it, key, value, iterData))
6156
+ renderOne(key, value, attrName);
6157
+ }, start, end);
6148
6158
  return r;
6149
6159
  }
6150
6160
  renderView(view, stack) {
@@ -6180,8 +6190,17 @@ var filterAlwaysTrue = (_v, _k, _seq) => true;
6180
6190
  var nullLoopWith = (seq) => ({ iterData: { seq } });
6181
6191
  var unpackLoopResult = (result, seq) => {
6182
6192
  const r = result ?? {};
6183
- return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end };
6193
+ return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end, keys: r.keys };
6184
6194
  };
6195
+ var imKeysIter = (seq, visit, keys) => {
6196
+ const attrName = isIndexed(seq) ? "si" : "sk";
6197
+ for (const key of keys)
6198
+ visit(key, seq.get(key), attrName);
6199
+ };
6200
+ var makeLoopCtx = (stack, filter) => ({
6201
+ lookup: (name) => stack.lookupBind(name),
6202
+ filter: (key, value, iterData) => filter.call(stack.it, key, value, iterData)
6203
+ });
6185
6204
  var imIndexedIter = (seq, visit, start, end) => {
6186
6205
  const [s, e] = normalizeRange(start, end, seq.size);
6187
6206
  for (let i = s;i < e; i++)
@@ -6729,11 +6748,11 @@ class IterInfo {
6729
6748
  return { seq, filter, loopWith, enricher };
6730
6749
  }
6731
6750
  enrichBinds(stack, key) {
6732
- const { seq, loopWith, enricher } = this.eval(stack);
6751
+ const { seq, filter, loopWith, enricher } = this.eval(stack);
6733
6752
  const value = seq?.get ? seq.get(key, null) : null;
6734
6753
  const binds = { key, value };
6735
6754
  if (enricher) {
6736
- const { iterData } = unpackLoopResult(loopWith.call(stack.it, seq), seq);
6755
+ const { iterData } = unpackLoopResult(loopWith.call(stack.it, seq, makeLoopCtx(stack, filter)), seq);
6737
6756
  enricher.call(stack.it, binds, key, value, iterData);
6738
6757
  }
6739
6758
  return binds;
@@ -8028,6 +8047,13 @@ function phaseOps(phase) {
8028
8047
  function resolveArgs(args, self) {
8029
8048
  return typeof args === "function" ? args(self) ?? [] : args ?? [];
8030
8049
  }
8050
+ function phaseHasBubble(phase) {
8051
+ if (!phase)
8052
+ return false;
8053
+ if (phase.bubble?.length)
8054
+ return true;
8055
+ return (phase.do ?? []).some((op) => op.type === "bubble");
8056
+ }
8031
8057
  function dispatchPhase(dispatcher, targetPath, phase, self) {
8032
8058
  if (!phase)
8033
8059
  return;
@@ -8320,7 +8346,8 @@ class FieldSet extends Field {
8320
8346
  }
8321
8347
  function mkCompField(field, scope, args) {
8322
8348
  const Comp = scope?.lookupComponent(field.type) ?? null;
8323
- console.assert(!scope || Comp !== null, "component not found", { field });
8349
+ if (Comp === null)
8350
+ console.warn(scope ? `component field "${field.name}": component "${field.type}" not found in scope` : `component field "${field.name}": cannot resolve component "${field.type}" — built without a registered scope (use ${field.type}.make({}) as the default, or build via a registered component)`);
8324
8351
  return Comp?.make({ ...field.args, ...args }, { scope }) ?? null;
8325
8352
  }
8326
8353
 
@@ -8469,6 +8496,7 @@ export {
8469
8496
  removeIn,
8470
8497
  remove,
8471
8498
  phaseOps,
8499
+ phaseHasBubble,
8472
8500
  mergeWith$1 as mergeWith,
8473
8501
  mergeDeepWith$1 as mergeDeepWith,
8474
8502
  mergeDeep$1 as mergeDeep,