@gjermundgaraba-designs/pixel-paper 0.18.0 → 0.20.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 (41) hide show
  1. package/DESIGN.md +26 -5
  2. package/dist/src/commandPalette/commandPalette.d.ts +2 -2
  3. package/dist/src/commandPalette/commandPalette.d.ts.map +1 -1
  4. package/dist/src/commandPalette/commandPalette.js +66 -42
  5. package/dist/src/commandPalette/match.d.ts +15 -5
  6. package/dist/src/commandPalette/match.d.ts.map +1 -1
  7. package/dist/src/commandPalette/match.js +17 -2
  8. package/dist/src/drag/drag.d.ts +3 -0
  9. package/dist/src/drag/drag.d.ts.map +1 -1
  10. package/dist/src/drag/drag.js +14 -10
  11. package/dist/src/index.d.ts +1 -0
  12. package/dist/src/index.d.ts.map +1 -1
  13. package/dist/src/index.js +1 -0
  14. package/dist/src/reel/index.d.ts +4 -0
  15. package/dist/src/reel/index.d.ts.map +1 -0
  16. package/dist/src/reel/index.js +2 -0
  17. package/dist/src/reel/layout.d.ts +180 -0
  18. package/dist/src/reel/layout.d.ts.map +1 -0
  19. package/dist/src/reel/layout.js +370 -0
  20. package/dist/src/reel/reel.d.ts +1158 -0
  21. package/dist/src/reel/reel.d.ts.map +1 -0
  22. package/dist/src/reel/reel.js +922 -0
  23. package/dist/src/sortable/sortable.d.ts +30 -30
  24. package/dist/src/sortable/sortable.d.ts.map +1 -1
  25. package/dist/src/sortable/sortable.js +2 -10
  26. package/dist/src/toast/toast.d.ts +12 -12
  27. package/dist/src/window/window.d.ts +2 -0
  28. package/dist/src/window/window.d.ts.map +1 -1
  29. package/dist/src/window/window.js +2 -1
  30. package/package.json +1 -1
  31. package/src/commandPalette/commandPalette.ts +71 -24
  32. package/src/commandPalette/match.ts +35 -6
  33. package/src/drag/drag.ts +19 -4
  34. package/src/index.ts +1 -0
  35. package/src/pixel-paper.css +236 -2
  36. package/src/reel/index.ts +20 -0
  37. package/src/reel/layout.ts +602 -0
  38. package/src/reel/reel.ts +1597 -0
  39. package/src/sortable/sortable.ts +8 -16
  40. package/src/tokens.css +5 -0
  41. package/src/window/window.ts +4 -0
package/src/drag/drag.ts CHANGED
@@ -1,7 +1,7 @@
1
- // Shared by Board and Sortable, the two users of @foldkit/ui's DragAndDrop: the phase a drag
2
- // is in, the DragAndDrop Messages both build, what a press lifts, Escape taken before anything
3
- // else, the lifted copy's measure, and the closest-centre drop target a sortable resolves for
4
- // a pointer.
1
+ // Shared by Board and Sortable, the two users of @foldkit/ui's DragAndDrop, and Reel, which
2
+ // holds its own: the phase a drag is in, the DragAndDrop Messages both build, what a press
3
+ // lifts, Escape taken before anything else, the pressing pointer's events, the lifted copy's
4
+ // measure, and the closest-centre drop target a sortable resolves for a pointer.
5
5
 
6
6
  import { DragAndDrop as UiDragAndDrop } from "@foldkit/ui";
7
7
  import { Array, Effect, Option, Order, Stream, pipe } from "effect";
@@ -110,6 +110,21 @@ export const escapeKeys = <Message>(message: Message): Stream.Stream<Message> =>
110
110
  options: { capture: true },
111
111
  });
112
112
 
113
+ // POINTER
114
+
115
+ /** The moves, release and cancel of the pointer `pointerId`, on the document; another
116
+ * pointer's are not the drag's. */
117
+ export const pressingPointer = (pointerId: number): Stream.Stream<PointerEvent> =>
118
+ Stream.filter(
119
+ Stream.mergeAll(
120
+ Array.map(["pointermove", "pointerup", "pointercancel"] as const, (type) =>
121
+ Stream.fromEventListener<PointerEvent>(document, type),
122
+ ),
123
+ { concurrency: "unbounded" },
124
+ ),
125
+ (event) => event.pointerId === pointerId,
126
+ );
127
+
113
128
  // LIFTED COPY
114
129
 
115
130
  /** Sizes the copy that follows the pointer to the element `sourceSelector` finds, and keeps the
package/src/index.ts CHANGED
@@ -65,6 +65,7 @@ export * as Prose from "./prose/index.ts";
65
65
  export * as QueryChip from "./queryChip/index.ts";
66
66
  export * as RadioChoices from "./radioChoices/index.ts";
67
67
  export * as Rail from "./rail/index.ts";
68
+ export * as Reel from "./reel/index.ts";
68
69
  export * as Remote from "./remote/index.ts";
69
70
  export * as Rows from "./rows/index.ts";
70
71
  export * as Scheme from "./scheme/index.ts";
@@ -2893,7 +2893,9 @@ dialog.veil:focus {
2893
2893
  align-items: center;
2894
2894
  gap: 10px;
2895
2895
  width: 100%;
2896
- padding: 8px 16px;
2896
+ /* on a page's tree a choice indents a step a level under the roots (--palette-level), never
2897
+ past a third of the row */
2898
+ padding: 8px 16px 8px calc(16px + min((var(--palette-level, 1) - 1) * 20px, 33%));
2897
2899
  font-family: var(--font-px);
2898
2900
  font-size: var(--type-control);
2899
2901
  text-transform: uppercase;
@@ -2923,6 +2925,11 @@ dialog.veil:focus {
2923
2925
  background-color: var(--head);
2924
2926
  }
2925
2927
 
2928
+ /* a choice a query keeps only to show where a found one sits: muted, still pickable */
2929
+ .palette-row.context:not([aria-selected="true"]) {
2930
+ color: var(--muted);
2931
+ }
2932
+
2926
2933
  .palette-row .hint {
2927
2934
  margin-left: auto;
2928
2935
  font-size: var(--type-state);
@@ -3433,7 +3440,8 @@ dialog.veil:focus {
3433
3440
  padding: 6px;
3434
3441
  }
3435
3442
 
3436
- .sortable-lift {
3443
+ .sortable-lift,
3444
+ .reel-lift {
3437
3445
  width: var(--lift-width, 320px);
3438
3446
  translate: var(--lift-x, 0) var(--lift-y, 0);
3439
3447
  }
@@ -5121,6 +5129,11 @@ dialog.veil:focus {
5121
5129
  user-select: none;
5122
5130
  }
5123
5131
 
5132
+ /* a lead (a reel's grip) trims the bar's left, as a panel's does */
5133
+ .window > .panel-tb:has(> .panel-lead) {
5134
+ padding-left: 3px;
5135
+ }
5136
+
5124
5137
  .window > .panel-tb h2 {
5125
5138
  flex: 1;
5126
5139
  min-width: 0;
@@ -5163,6 +5176,227 @@ dialog.veil:focus {
5163
5176
  min-width: 0;
5164
5177
  }
5165
5178
 
5179
+ /* ---------- the reel: a scrolling row of columns of panes ---------- */
5180
+
5181
+ /* the reel takes its height from whatever holds it; the held copy hangs from it, outside the
5182
+ scroller, whose containment would hold a fixed copy inside it */
5183
+ .reel {
5184
+ height: 100%;
5185
+ min-height: 0;
5186
+ }
5187
+
5188
+ /* the scroller, sideways and, where a column is taller than the view, down. Its size is what
5189
+ a column's width (100cqw) and least height (100cqh) are shares of, so it takes no padding */
5190
+ .reel-view {
5191
+ height: 100%;
5192
+ overflow: auto;
5193
+ overscroll-behavior: contain;
5194
+ container-type: size;
5195
+ }
5196
+
5197
+ /* the columns in a row, and the panes' containing block. Each pane is pinned to its slot by
5198
+ anchor positioning, so a move changes where a pane is drawn and never its place in the page */
5199
+ .reel-track {
5200
+ position: relative;
5201
+ display: flex;
5202
+ align-items: flex-start;
5203
+ width: max-content;
5204
+ min-width: 100%;
5205
+ min-height: 100%;
5206
+ padding-bottom: var(--reel-gutter);
5207
+ }
5208
+
5209
+ .reel-gap {
5210
+ position: relative;
5211
+ flex: none;
5212
+ align-self: stretch;
5213
+ width: var(--reel-gutter);
5214
+ }
5215
+
5216
+ /* a column is width × (view − gutter) − gutter wide, so columns whose widths add up to one
5217
+ fill the view with a gutter between each and at either side; never under the minimum. It is
5218
+ the view's height, and taller where its panes need more, which the view scrolls to */
5219
+ .reel-column {
5220
+ display: flex;
5221
+ flex: none;
5222
+ flex-direction: column;
5223
+ width: max(
5224
+ var(--reel-column-min),
5225
+ var(--reel-width) * (100cqw - var(--reel-gutter)) - var(--reel-gutter)
5226
+ );
5227
+ min-height: calc(100cqh - var(--reel-gutter));
5228
+ }
5229
+
5230
+ /* the column's head, in the gutter above its panes: its grip and its width */
5231
+ .reel-column-head {
5232
+ display: flex;
5233
+ flex: none;
5234
+ align-items: center;
5235
+ gap: var(--space-s);
5236
+ padding-block: 4px;
5237
+ }
5238
+
5239
+ .reel-width {
5240
+ font-family: var(--font-px);
5241
+ font-size: var(--type-state);
5242
+ letter-spacing: var(--tracking-state);
5243
+ text-transform: uppercase;
5244
+ color: var(--muted);
5245
+ }
5246
+
5247
+ /* a slot holds a pane's place, its share of the column by weight; the pane is drawn over it */
5248
+ .reel-slot {
5249
+ position: relative;
5250
+ flex: var(--reel-weight, 1) 1 0;
5251
+ min-height: var(--reel-pane-min);
5252
+ anchor-name: var(--reel-anchor);
5253
+ }
5254
+
5255
+ /* the splitters: the gutter between two panes, and the one after each column */
5256
+ .reel-split {
5257
+ position: relative;
5258
+ flex: none;
5259
+ height: var(--reel-gutter);
5260
+ cursor: row-resize;
5261
+ touch-action: none;
5262
+ }
5263
+
5264
+ .reel-resize {
5265
+ cursor: col-resize;
5266
+ touch-action: none;
5267
+ }
5268
+
5269
+ /* a splitter shows itself as a 2px ink line along the middle of its gutter while it is
5270
+ hovered or focused; focused, the ring is the system's */
5271
+ .reel-resize::after,
5272
+ .reel-split::after {
5273
+ content: "";
5274
+ position: absolute;
5275
+ background: var(--ink);
5276
+ opacity: 0;
5277
+ pointer-events: none;
5278
+ }
5279
+
5280
+ .reel-resize::after {
5281
+ top: 0;
5282
+ bottom: var(--reel-gutter);
5283
+ left: calc(50% - 1px);
5284
+ width: var(--line);
5285
+ }
5286
+
5287
+ .reel-split::after {
5288
+ left: 0;
5289
+ right: 0;
5290
+ top: calc(50% - 1px);
5291
+ height: var(--line);
5292
+ }
5293
+
5294
+ .reel-resize:is(:hover, :focus-visible)::after,
5295
+ .reel-split:is(:hover, :focus-visible)::after {
5296
+ opacity: 1;
5297
+ }
5298
+
5299
+ .reel-resize:focus-visible,
5300
+ .reel-split:focus-visible {
5301
+ outline-offset: -2px;
5302
+ }
5303
+
5304
+ /* where a drop would land: a 2px dashed ink line down the gap it would open, or across the
5305
+ gutter between the panes it would join, drawn over the layout so nothing moves under it */
5306
+ .reel-gap.drop-here::before {
5307
+ content: "";
5308
+ position: absolute;
5309
+ top: 0;
5310
+ bottom: var(--reel-gutter);
5311
+ left: calc(50% - 1px);
5312
+ border-left: var(--line) dashed var(--ink);
5313
+ pointer-events: none;
5314
+ }
5315
+
5316
+ .reel-slot:is(.drop-before, .drop-after)::before {
5317
+ content: "";
5318
+ position: absolute;
5319
+ left: 0;
5320
+ right: 0;
5321
+ border-top: var(--line) dashed var(--ink);
5322
+ pointer-events: none;
5323
+ }
5324
+
5325
+ .reel-slot.drop-before::before {
5326
+ top: calc(var(--reel-gutter) / -2 - 1px);
5327
+ }
5328
+
5329
+ .reel-slot.drop-after::before {
5330
+ bottom: calc(var(--reel-gutter) / -2 - 1px);
5331
+ }
5332
+
5333
+ /* the panes' layer is no box of its own: each pane's containing block is the track */
5334
+ .reel-panes {
5335
+ display: contents;
5336
+ }
5337
+
5338
+ /* a pane or a column scrolled into view keeps its gutter showing */
5339
+ .reel-pane,
5340
+ .reel-column {
5341
+ scroll-margin: var(--reel-gutter);
5342
+ }
5343
+
5344
+ .reel-pane {
5345
+ position: absolute;
5346
+ position-anchor: var(--reel-anchor);
5347
+ top: anchor(top);
5348
+ right: anchor(right);
5349
+ bottom: anchor(bottom);
5350
+ left: anchor(left);
5351
+ min-width: 0;
5352
+ }
5353
+
5354
+ /* the focused pane lifts onto the card shadow; taking focus itself (it holds no control), it
5355
+ keeps the system's ring outside its window */
5356
+ .reel-pane.focused > .window {
5357
+ box-shadow: var(--shadow-card);
5358
+ }
5359
+
5360
+ .reel-pane:focus-visible {
5361
+ outline: 0;
5362
+ }
5363
+
5364
+ .reel-pane:focus-visible > .window {
5365
+ outline: var(--line) solid var(--ink);
5366
+ outline-offset: 2px;
5367
+ }
5368
+
5369
+ .reel-pane.drag-dim {
5370
+ opacity: 0.4;
5371
+ }
5372
+
5373
+ /* only the grips lift: a pane's text selects and its terminal takes the pointer */
5374
+ [data-reel-grip] {
5375
+ cursor: grab;
5376
+ touch-action: none;
5377
+ }
5378
+
5379
+ /* the held copy, following the pointer from the window's corner (its transform puts it at the
5380
+ pointer): the title bars alone, tinted head on the card shadow */
5381
+ .reel-lift {
5382
+ position: fixed;
5383
+ top: 0;
5384
+ left: 0;
5385
+ z-index: 9999;
5386
+ pointer-events: none;
5387
+ }
5388
+
5389
+ .reel-lifted {
5390
+ display: grid;
5391
+ gap: var(--space-s);
5392
+ }
5393
+
5394
+ .reel-lifted .window {
5395
+ height: auto;
5396
+ background: var(--head);
5397
+ box-shadow: var(--shadow-card);
5398
+ }
5399
+
5166
5400
  /* ---------- images and media: a picture or a player in a 2px ink frame, at rest ---------- */
5167
5401
 
5168
5402
  /* 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";