@gjermundgaraba-designs/pixel-paper 0.17.0 → 0.19.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 (57) hide show
  1. package/DESIGN.md +41 -12
  2. package/dist/src/drag/drag.d.ts +3 -0
  3. package/dist/src/drag/drag.d.ts.map +1 -1
  4. package/dist/src/drag/drag.js +14 -10
  5. package/dist/src/glyph/glyph.d.ts +3 -1
  6. package/dist/src/glyph/glyph.d.ts.map +1 -1
  7. package/dist/src/glyph/glyph.js +2 -1
  8. package/dist/src/glyph/glyphs.d.ts +3 -0
  9. package/dist/src/glyph/glyphs.d.ts.map +1 -1
  10. package/dist/src/glyph/glyphs.js +4 -3
  11. package/dist/src/identity/identity.d.ts +2 -0
  12. package/dist/src/identity/identity.d.ts.map +1 -1
  13. package/dist/src/identity/identity.js +8 -2
  14. package/dist/src/index.d.ts +1 -0
  15. package/dist/src/index.d.ts.map +1 -1
  16. package/dist/src/index.js +1 -0
  17. package/dist/src/meter/meter.d.ts +5 -1
  18. package/dist/src/meter/meter.d.ts.map +1 -1
  19. package/dist/src/meter/meter.js +15 -3
  20. package/dist/src/monogram/monogram.d.ts +7 -2
  21. package/dist/src/monogram/monogram.d.ts.map +1 -1
  22. package/dist/src/monogram/monogram.js +24 -6
  23. package/dist/src/panel/panel.d.ts +5 -1
  24. package/dist/src/panel/panel.d.ts.map +1 -1
  25. package/dist/src/panel/panel.js +7 -3
  26. package/dist/src/reel/index.d.ts +4 -0
  27. package/dist/src/reel/index.d.ts.map +1 -0
  28. package/dist/src/reel/index.js +2 -0
  29. package/dist/src/reel/layout.d.ts +180 -0
  30. package/dist/src/reel/layout.d.ts.map +1 -0
  31. package/dist/src/reel/layout.js +370 -0
  32. package/dist/src/reel/reel.d.ts +1158 -0
  33. package/dist/src/reel/reel.d.ts.map +1 -0
  34. package/dist/src/reel/reel.js +922 -0
  35. package/dist/src/sortable/sortable.d.ts +30 -30
  36. package/dist/src/sortable/sortable.d.ts.map +1 -1
  37. package/dist/src/sortable/sortable.js +2 -10
  38. package/dist/src/toast/toast.d.ts +12 -12
  39. package/dist/src/window/window.d.ts +2 -0
  40. package/dist/src/window/window.d.ts.map +1 -1
  41. package/dist/src/window/window.js +2 -1
  42. package/package.json +1 -1
  43. package/src/drag/drag.ts +19 -4
  44. package/src/glyph/glyph.ts +2 -1
  45. package/src/glyph/glyphs.ts +6 -4
  46. package/src/identity/identity.ts +10 -2
  47. package/src/index.ts +1 -0
  48. package/src/meter/meter.ts +18 -2
  49. package/src/monogram/monogram.ts +46 -6
  50. package/src/panel/panel.ts +21 -3
  51. package/src/pixel-paper.css +249 -2
  52. package/src/reel/index.ts +20 -0
  53. package/src/reel/layout.ts +602 -0
  54. package/src/reel/reel.ts +1597 -0
  55. package/src/sortable/sortable.ts +8 -16
  56. package/src/tokens.css +5 -0
  57. package/src/window/window.ts +4 -0
@@ -14,6 +14,10 @@ export type ViewConfig = Readonly<{
14
14
  id: string;
15
15
  /** Pixel caps in the inverted title bar. */
16
16
  title: string;
17
+ /** What screen readers call the panel where its title repeats among its siblings (panels
18
+ * titled by a kind, one per account): the title and what tells this one apart. The
19
+ * title names it otherwise. */
20
+ name?: string;
17
21
  /** A quiet IconButton before the title, such as the grip a Sortable of panels hands each
18
22
  * panel. On the ink bar it prints as `actions` do. */
19
23
  lead?: ReadonlyArray<Html | string>;
@@ -33,13 +37,27 @@ export type ViewConfig = Readonly<{
33
37
  export const titleId = (id: string): string => `${id}-title`;
34
38
 
35
39
  /** The unit of the system: paper in a 2px ink box on a 5px print shadow, under a title bar. A
36
- * region landmark (`<section>`) named by its `h2` title, keyed by its `id`. */
40
+ * region landmark (`<section>`) named by its `h2` title, or by `name`, keyed by its `id`. */
37
41
  export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html => {
38
- const { id, title, lead = [], meta, actions = [], body, foot = [], isFlush = false } = config;
42
+ const {
43
+ id,
44
+ title,
45
+ name,
46
+ lead = [],
47
+ meta,
48
+ actions = [],
49
+ body,
50
+ foot = [],
51
+ isFlush = false,
52
+ } = config;
39
53
 
40
54
  return h.keyed("section")(
41
55
  id,
42
- [h.Id(id), h.AriaLabelledBy(titleId(id)), h.Class("panel")],
56
+ [
57
+ h.Id(id),
58
+ name === undefined ? h.AriaLabelledBy(titleId(id)) : h.AriaLabel(name),
59
+ h.Class("panel"),
60
+ ],
43
61
  [
44
62
  h.header(
45
63
  [h.Class("panel-tb")],
@@ -3433,7 +3433,8 @@ dialog.veil:focus {
3433
3433
  padding: 6px;
3434
3434
  }
3435
3435
 
3436
- .sortable-lift {
3436
+ .sortable-lift,
3437
+ .reel-lift {
3437
3438
  width: var(--lift-width, 320px);
3438
3439
  translate: var(--lift-x, 0) var(--lift-y, 0);
3439
3440
  }
@@ -3655,6 +3656,16 @@ dialog.veil:focus {
3655
3656
  flex-wrap: wrap;
3656
3657
  }
3657
3658
 
3659
+ /* a toned meter's lit cells take its reading's tone, amber from 80% and red at the maximum,
3660
+ unless the reading is stale */
3661
+ .meter-row.toned.high:not(.stale) .cells .cell.on {
3662
+ background: var(--amber);
3663
+ }
3664
+
3665
+ .meter-row.toned.full:not(.stale) .cells .cell.on {
3666
+ background: var(--red);
3667
+ }
3668
+
3658
3669
  /* a stale reading prints its cells muted: the number is last known, not current */
3659
3670
  .meter-row.stale .cells .cell.on {
3660
3671
  background: var(--muted);
@@ -4522,7 +4533,7 @@ dialog.veil:focus {
4522
4533
  position: absolute;
4523
4534
  }
4524
4535
 
4525
- /* ---------- monograms: letters in an ink square, where the system draws no logos ---------- */
4536
+ /* ---------- monograms: letters in an ink square, or a mark the product draws in cells ---------- */
4526
4537
 
4527
4538
  .monogram {
4528
4539
  display: inline-grid;
@@ -4551,6 +4562,16 @@ dialog.veil:focus {
4551
4562
  color: var(--paper);
4552
4563
  }
4553
4564
 
4565
+ /* a mark drawn in cells prints bare, in ink, in the square the letters would take */
4566
+ .monogram.drawn {
4567
+ border: 0;
4568
+ background: none;
4569
+ }
4570
+
4571
+ .monogram.drawn.current {
4572
+ background: var(--ink);
4573
+ }
4574
+
4554
4575
  /* ---------- identity: who or what, as its monogram beside its name ---------- */
4555
4576
 
4556
4577
  .identity {
@@ -5101,6 +5122,11 @@ dialog.veil:focus {
5101
5122
  user-select: none;
5102
5123
  }
5103
5124
 
5125
+ /* a lead (a reel's grip) trims the bar's left, as a panel's does */
5126
+ .window > .panel-tb:has(> .panel-lead) {
5127
+ padding-left: 3px;
5128
+ }
5129
+
5104
5130
  .window > .panel-tb h2 {
5105
5131
  flex: 1;
5106
5132
  min-width: 0;
@@ -5143,6 +5169,227 @@ dialog.veil:focus {
5143
5169
  min-width: 0;
5144
5170
  }
5145
5171
 
5172
+ /* ---------- the reel: a scrolling row of columns of panes ---------- */
5173
+
5174
+ /* the reel takes its height from whatever holds it; the held copy hangs from it, outside the
5175
+ scroller, whose containment would hold a fixed copy inside it */
5176
+ .reel {
5177
+ height: 100%;
5178
+ min-height: 0;
5179
+ }
5180
+
5181
+ /* the scroller, sideways and, where a column is taller than the view, down. Its size is what
5182
+ a column's width (100cqw) and least height (100cqh) are shares of, so it takes no padding */
5183
+ .reel-view {
5184
+ height: 100%;
5185
+ overflow: auto;
5186
+ overscroll-behavior: contain;
5187
+ container-type: size;
5188
+ }
5189
+
5190
+ /* the columns in a row, and the panes' containing block. Each pane is pinned to its slot by
5191
+ anchor positioning, so a move changes where a pane is drawn and never its place in the page */
5192
+ .reel-track {
5193
+ position: relative;
5194
+ display: flex;
5195
+ align-items: flex-start;
5196
+ width: max-content;
5197
+ min-width: 100%;
5198
+ min-height: 100%;
5199
+ padding-bottom: var(--reel-gutter);
5200
+ }
5201
+
5202
+ .reel-gap {
5203
+ position: relative;
5204
+ flex: none;
5205
+ align-self: stretch;
5206
+ width: var(--reel-gutter);
5207
+ }
5208
+
5209
+ /* a column is width × (view − gutter) − gutter wide, so columns whose widths add up to one
5210
+ fill the view with a gutter between each and at either side; never under the minimum. It is
5211
+ the view's height, and taller where its panes need more, which the view scrolls to */
5212
+ .reel-column {
5213
+ display: flex;
5214
+ flex: none;
5215
+ flex-direction: column;
5216
+ width: max(
5217
+ var(--reel-column-min),
5218
+ var(--reel-width) * (100cqw - var(--reel-gutter)) - var(--reel-gutter)
5219
+ );
5220
+ min-height: calc(100cqh - var(--reel-gutter));
5221
+ }
5222
+
5223
+ /* the column's head, in the gutter above its panes: its grip and its width */
5224
+ .reel-column-head {
5225
+ display: flex;
5226
+ flex: none;
5227
+ align-items: center;
5228
+ gap: var(--space-s);
5229
+ padding-block: 4px;
5230
+ }
5231
+
5232
+ .reel-width {
5233
+ font-family: var(--font-px);
5234
+ font-size: var(--type-state);
5235
+ letter-spacing: var(--tracking-state);
5236
+ text-transform: uppercase;
5237
+ color: var(--muted);
5238
+ }
5239
+
5240
+ /* a slot holds a pane's place, its share of the column by weight; the pane is drawn over it */
5241
+ .reel-slot {
5242
+ position: relative;
5243
+ flex: var(--reel-weight, 1) 1 0;
5244
+ min-height: var(--reel-pane-min);
5245
+ anchor-name: var(--reel-anchor);
5246
+ }
5247
+
5248
+ /* the splitters: the gutter between two panes, and the one after each column */
5249
+ .reel-split {
5250
+ position: relative;
5251
+ flex: none;
5252
+ height: var(--reel-gutter);
5253
+ cursor: row-resize;
5254
+ touch-action: none;
5255
+ }
5256
+
5257
+ .reel-resize {
5258
+ cursor: col-resize;
5259
+ touch-action: none;
5260
+ }
5261
+
5262
+ /* a splitter shows itself as a 2px ink line along the middle of its gutter while it is
5263
+ hovered or focused; focused, the ring is the system's */
5264
+ .reel-resize::after,
5265
+ .reel-split::after {
5266
+ content: "";
5267
+ position: absolute;
5268
+ background: var(--ink);
5269
+ opacity: 0;
5270
+ pointer-events: none;
5271
+ }
5272
+
5273
+ .reel-resize::after {
5274
+ top: 0;
5275
+ bottom: var(--reel-gutter);
5276
+ left: calc(50% - 1px);
5277
+ width: var(--line);
5278
+ }
5279
+
5280
+ .reel-split::after {
5281
+ left: 0;
5282
+ right: 0;
5283
+ top: calc(50% - 1px);
5284
+ height: var(--line);
5285
+ }
5286
+
5287
+ .reel-resize:is(:hover, :focus-visible)::after,
5288
+ .reel-split:is(:hover, :focus-visible)::after {
5289
+ opacity: 1;
5290
+ }
5291
+
5292
+ .reel-resize:focus-visible,
5293
+ .reel-split:focus-visible {
5294
+ outline-offset: -2px;
5295
+ }
5296
+
5297
+ /* where a drop would land: a 2px dashed ink line down the gap it would open, or across the
5298
+ gutter between the panes it would join, drawn over the layout so nothing moves under it */
5299
+ .reel-gap.drop-here::before {
5300
+ content: "";
5301
+ position: absolute;
5302
+ top: 0;
5303
+ bottom: var(--reel-gutter);
5304
+ left: calc(50% - 1px);
5305
+ border-left: var(--line) dashed var(--ink);
5306
+ pointer-events: none;
5307
+ }
5308
+
5309
+ .reel-slot:is(.drop-before, .drop-after)::before {
5310
+ content: "";
5311
+ position: absolute;
5312
+ left: 0;
5313
+ right: 0;
5314
+ border-top: var(--line) dashed var(--ink);
5315
+ pointer-events: none;
5316
+ }
5317
+
5318
+ .reel-slot.drop-before::before {
5319
+ top: calc(var(--reel-gutter) / -2 - 1px);
5320
+ }
5321
+
5322
+ .reel-slot.drop-after::before {
5323
+ bottom: calc(var(--reel-gutter) / -2 - 1px);
5324
+ }
5325
+
5326
+ /* the panes' layer is no box of its own: each pane's containing block is the track */
5327
+ .reel-panes {
5328
+ display: contents;
5329
+ }
5330
+
5331
+ /* a pane or a column scrolled into view keeps its gutter showing */
5332
+ .reel-pane,
5333
+ .reel-column {
5334
+ scroll-margin: var(--reel-gutter);
5335
+ }
5336
+
5337
+ .reel-pane {
5338
+ position: absolute;
5339
+ position-anchor: var(--reel-anchor);
5340
+ top: anchor(top);
5341
+ right: anchor(right);
5342
+ bottom: anchor(bottom);
5343
+ left: anchor(left);
5344
+ min-width: 0;
5345
+ }
5346
+
5347
+ /* the focused pane lifts onto the card shadow; taking focus itself (it holds no control), it
5348
+ keeps the system's ring outside its window */
5349
+ .reel-pane.focused > .window {
5350
+ box-shadow: var(--shadow-card);
5351
+ }
5352
+
5353
+ .reel-pane:focus-visible {
5354
+ outline: 0;
5355
+ }
5356
+
5357
+ .reel-pane:focus-visible > .window {
5358
+ outline: var(--line) solid var(--ink);
5359
+ outline-offset: 2px;
5360
+ }
5361
+
5362
+ .reel-pane.drag-dim {
5363
+ opacity: 0.4;
5364
+ }
5365
+
5366
+ /* only the grips lift: a pane's text selects and its terminal takes the pointer */
5367
+ [data-reel-grip] {
5368
+ cursor: grab;
5369
+ touch-action: none;
5370
+ }
5371
+
5372
+ /* the held copy, following the pointer from the window's corner (its transform puts it at the
5373
+ pointer): the title bars alone, tinted head on the card shadow */
5374
+ .reel-lift {
5375
+ position: fixed;
5376
+ top: 0;
5377
+ left: 0;
5378
+ z-index: 9999;
5379
+ pointer-events: none;
5380
+ }
5381
+
5382
+ .reel-lifted {
5383
+ display: grid;
5384
+ gap: var(--space-s);
5385
+ }
5386
+
5387
+ .reel-lifted .window {
5388
+ height: auto;
5389
+ background: var(--head);
5390
+ box-shadow: var(--shadow-card);
5391
+ }
5392
+
5146
5393
  /* ---------- images and media: a picture or a player in a 2px ink frame, at rest ---------- */
5147
5394
 
5148
5395
  /* a figure sits at rest wherever it is placed (a panel, prose): no print shadow. The frame
@@ -0,0 +1,20 @@
1
+ export { Layout, OutMessage, Pane, apply } from "./layout.ts";
2
+ export {
3
+ Action,
4
+ AnnounceReel,
5
+ FocusGrip,
6
+ FocusPane,
7
+ Message,
8
+ Model,
9
+ RestoreFocus,
10
+ ScrollIntoView,
11
+ SizeLiftedPane,
12
+ focusedPaneId,
13
+ init,
14
+ perform,
15
+ reconcile,
16
+ subscriptions,
17
+ update,
18
+ view,
19
+ } from "./reel.ts";
20
+ export type { InitConfig, PaneRender, UpdateContext, ViewInputs } from "./reel.ts";