@gjermundgaraba-designs/pixel-paper 0.23.0 → 0.25.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.
@@ -12,15 +12,18 @@ export declare const TipState: import("foldkit/schema").TaggedUnion<{
12
12
  readonly Cold: {};
13
13
  readonly Waiting: {
14
14
  text: Schema.String;
15
+ maybeKey: Schema.Option<Schema.String>;
15
16
  side: Schema.Literals<readonly ["Top", "Right"]>;
16
17
  };
17
18
  readonly Shown: {
18
19
  text: Schema.String;
20
+ maybeKey: Schema.Option<Schema.String>;
19
21
  side: Schema.Literals<readonly ["Top", "Right"]>;
20
22
  trigger: Schema.Literals<readonly ["Pointer", "Focus"]>;
21
23
  };
22
24
  readonly Leaving: {
23
25
  text: Schema.String;
26
+ maybeKey: Schema.Option<Schema.String>;
24
27
  side: Schema.Literals<readonly ["Top", "Right"]>;
25
28
  trigger: Schema.Literals<readonly ["Pointer", "Focus"]>;
26
29
  };
@@ -36,15 +39,18 @@ export declare const Model: Schema.Struct<{
36
39
  readonly Cold: {};
37
40
  readonly Waiting: {
38
41
  text: Schema.String;
42
+ maybeKey: Schema.Option<Schema.String>;
39
43
  side: Schema.Literals<readonly ["Top", "Right"]>;
40
44
  };
41
45
  readonly Shown: {
42
46
  text: Schema.String;
47
+ maybeKey: Schema.Option<Schema.String>;
43
48
  side: Schema.Literals<readonly ["Top", "Right"]>;
44
49
  trigger: Schema.Literals<readonly ["Pointer", "Focus"]>;
45
50
  };
46
51
  readonly Leaving: {
47
52
  text: Schema.String;
53
+ maybeKey: Schema.Option<Schema.String>;
48
54
  side: Schema.Literals<readonly ["Top", "Right"]>;
49
55
  trigger: Schema.Literals<readonly ["Pointer", "Focus"]>;
50
56
  };
@@ -56,12 +62,14 @@ export type Model = typeof Model.Type;
56
62
  export declare const Message: import("foldkit/message").MessageUnion<{
57
63
  readonly EnteredTipTarget: {
58
64
  text: Schema.String;
65
+ maybeKey: Schema.Option<Schema.String>;
59
66
  side: Schema.Literals<readonly ["Top", "Right"]>;
60
67
  };
61
68
  readonly LeftTipTarget: {};
62
69
  readonly EnteredTip: {};
63
70
  readonly FocusedTipTarget: {
64
71
  text: Schema.String;
72
+ maybeKey: Schema.Option<Schema.String>;
65
73
  side: Schema.Literals<readonly ["Top", "Right"]>;
66
74
  };
67
75
  readonly BlurredTipTarget: {};
@@ -136,16 +144,19 @@ export declare const update: (model: Model, message: Message) => Readonly<{
136
144
  } | {
137
145
  readonly _tag: "Leaving";
138
146
  readonly text: string;
147
+ readonly maybeKey: Option.Option<string>;
139
148
  readonly side: "Right" | "Top";
140
149
  readonly trigger: "Focus" | "Pointer";
141
150
  } | {
142
151
  readonly _tag: "Shown";
143
152
  readonly text: string;
153
+ readonly maybeKey: Option.Option<string>;
144
154
  readonly side: "Right" | "Top";
145
155
  readonly trigger: "Focus" | "Pointer";
146
156
  } | {
147
157
  readonly _tag: "Waiting";
148
158
  readonly text: string;
159
+ readonly maybeKey: Option.Option<string>;
149
160
  readonly side: "Right" | "Top";
150
161
  } | {
151
162
  readonly _tag: "Warm";
@@ -171,10 +182,12 @@ export declare const update: (model: Model, message: Message) => Readonly<{
171
182
  } | {
172
183
  readonly _tag: "EnteredTipTarget";
173
184
  readonly text: string;
185
+ readonly maybeKey: Option.Option<string>;
174
186
  readonly side: "Right" | "Top";
175
187
  } | {
176
188
  readonly _tag: "FocusedTipTarget";
177
189
  readonly text: string;
190
+ readonly maybeKey: Option.Option<string>;
178
191
  readonly side: "Right" | "Top";
179
192
  } | {
180
193
  readonly _tag: "LeftTipTarget";
@@ -210,16 +223,19 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
210
223
  } | {
211
224
  readonly _tag: "Leaving";
212
225
  readonly text: string;
226
+ readonly maybeKey: Option.Option<string>;
213
227
  readonly side: "Right" | "Top";
214
228
  readonly trigger: "Focus" | "Pointer";
215
229
  } | {
216
230
  readonly _tag: "Shown";
217
231
  readonly text: string;
232
+ readonly maybeKey: Option.Option<string>;
218
233
  readonly side: "Right" | "Top";
219
234
  readonly trigger: "Focus" | "Pointer";
220
235
  } | {
221
236
  readonly _tag: "Waiting";
222
237
  readonly text: string;
238
+ readonly maybeKey: Option.Option<string>;
223
239
  readonly side: "Right" | "Top";
224
240
  } | {
225
241
  readonly _tag: "Warm";
@@ -244,10 +260,12 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
244
260
  } | {
245
261
  readonly _tag: "EnteredTipTarget";
246
262
  readonly text: string;
263
+ readonly maybeKey: Option.Option<string>;
247
264
  readonly side: "Right" | "Top";
248
265
  } | {
249
266
  readonly _tag: "FocusedTipTarget";
250
267
  readonly text: string;
268
+ readonly maybeKey: Option.Option<string>;
251
269
  readonly side: "Right" | "Top";
252
270
  } | {
253
271
  readonly _tag: "LeftTipTarget";
@@ -268,16 +286,19 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
268
286
  } | {
269
287
  readonly _tag: "Leaving";
270
288
  readonly text: string;
289
+ readonly maybeKey: Option.Option<string>;
271
290
  readonly side: "Right" | "Top";
272
291
  readonly trigger: "Focus" | "Pointer";
273
292
  } | {
274
293
  readonly _tag: "Shown";
275
294
  readonly text: string;
295
+ readonly maybeKey: Option.Option<string>;
276
296
  readonly side: "Right" | "Top";
277
297
  readonly trigger: "Focus" | "Pointer";
278
298
  } | {
279
299
  readonly _tag: "Waiting";
280
300
  readonly text: string;
301
+ readonly maybeKey: Option.Option<string>;
281
302
  readonly side: "Right" | "Top";
282
303
  } | {
283
304
  readonly _tag: "Warm";
@@ -302,10 +323,12 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
302
323
  } | {
303
324
  readonly _tag: "EnteredTipTarget";
304
325
  readonly text: string;
326
+ readonly maybeKey: Option.Option<string>;
305
327
  readonly side: "Right" | "Top";
306
328
  } | {
307
329
  readonly _tag: "FocusedTipTarget";
308
330
  readonly text: string;
331
+ readonly maybeKey: Option.Option<string>;
309
332
  readonly side: "Right" | "Top";
310
333
  } | {
311
334
  readonly _tag: "LeftTipTarget";
@@ -321,7 +344,8 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
321
344
  } extends infer T ? { readonly [Key in keyof T as `${Prefix}${Capitalize<Key>}`]: T[Key]; } : never;
322
345
  /**
323
346
  * The page's one tip: `div.tip[popover="manual"][aria-hidden="true"]`, which shows the words
324
- * of any control carrying `data-tip` (IconButton, the glyph switch, a board's grip). Render it
347
+ * of any control carrying `data-tip` (IconButton, the glyph switch, a board's grip), then the
348
+ * key in its `data-tip-key` where it has one (an IconButton's `keyHint`). Render it
325
349
  * once, beside the Announcer, and lift `subscriptions(keyPrefix)`. It opens on a resting
326
350
  * pointer after a moment (at once while another has just closed), never on touch, and on
327
351
  * keyboard focus; it can be hovered; leaving, blur, a press or any key closes it, and Escape
@@ -340,16 +364,19 @@ export declare const view: import("foldkit/submodel").View<{
340
364
  } | {
341
365
  readonly _tag: "Leaving";
342
366
  readonly text: string;
367
+ readonly maybeKey: Option.Option<string>;
343
368
  readonly side: "Right" | "Top";
344
369
  readonly trigger: "Focus" | "Pointer";
345
370
  } | {
346
371
  readonly _tag: "Shown";
347
372
  readonly text: string;
373
+ readonly maybeKey: Option.Option<string>;
348
374
  readonly side: "Right" | "Top";
349
375
  readonly trigger: "Focus" | "Pointer";
350
376
  } | {
351
377
  readonly _tag: "Waiting";
352
378
  readonly text: string;
379
+ readonly maybeKey: Option.Option<string>;
353
380
  readonly side: "Right" | "Top";
354
381
  } | {
355
382
  readonly _tag: "Warm";
@@ -374,10 +401,12 @@ export declare const view: import("foldkit/submodel").View<{
374
401
  } | {
375
402
  readonly _tag: "EnteredTipTarget";
376
403
  readonly text: string;
404
+ readonly maybeKey: Option.Option<string>;
377
405
  readonly side: "Right" | "Top";
378
406
  } | {
379
407
  readonly _tag: "FocusedTipTarget";
380
408
  readonly text: string;
409
+ readonly maybeKey: Option.Option<string>;
381
410
  readonly side: "Right" | "Top";
382
411
  } | {
383
412
  readonly _tag: "LeftTipTarget";
@@ -1 +1 @@
1
- {"version":3,"file":"tips.d.ts","sourceRoot":"","sources":["../../../src/tips/tips.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,MAAM,EAAiB,MAAM,EAAE,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAC9F,OAAO,KAAK,OAAO,MAAM,iBAAiB,CAAC;AAQ3C,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAW9C,iEAAiE;AACjE,eAAO,MAAM,OAAO,gDAAwC,CAAC;AAC7D,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAK1C;;+FAE+F;AAC/F,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;EAMnB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAE5C;sDACsD;AACtD,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;EAIhB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC;AAItC,uDAAuD;AACvD,eAAO,MAAM,OAAO;;QACE,IAAI;QAAiB,IAAI;;;;;QAGzB,IAAI;QAAiB,IAAI;;;;;;QAIf,OAAO;;;QACR,OAAO;;;QACN,OAAO;;;;EAGrC,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAI1C,2FAA2F;AAC3F,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAElD,yCAAyC;AACzC,eAAO,MAAM,IAAI,WAAY,UAAU,KAAG,KAIxC,CAAC;AAIH,oDAAoD;AACpD,eAAO,MAAM,EAAE,UAAW,KAAK,KAAG,MAAkB,CAAC;AA2DrD;;;0CAG0C;AAC1C,eAAO,MAAM,OAAO;;;;;;iBAmBlB,CAAC;AAEH,sBAAsB;AACtB,eAAO,MAAM,OAAO;;;;iBAQlB,CAAC;AAEH,mCAAmC;AACnC,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAC;AAEH,iDAAiD;AACjD,eAAO,MAAM,gBAAgB;;;;;iBAK3B,CAAC;AAEH,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAC;AAuEH,6EAA6E;AAC7E,eAAO,MAAM,MAAM,UAAW,KAAK,WAAW,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiDjD,CAAC;AAIL,KAAK,aAAa,GACd,OAAO,OAAO,CAAC,gBAAgB,CAAC,IAAI,GACpC,OAAO,OAAO,CAAC,aAAa,CAAC,IAAI,GACjC,OAAO,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;AAoBnC;gEACgE;AAChE,eAAO,MAAM,kBAAkB,UACtB,MAAM,SACN,YAAY,KAClB,MAAM,CAAC,MAAM,CAAC,aAAa,CAkB7B,CAAC;AAUF;kCACkC;AAClC,eAAO,MAAM,eAAe,UAAW,UAAU,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAQtE,CAAC;AAEJ;;;0CAG0C;AAC1C,eAAO,MAAM,UAAU,UAAW,aAAa,WAAW,OAAO,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAUxF,CAAC;AAkEF;;;8BAG8B;AAC9B,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,aAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mGAC7B,CAAC;AAW1C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAef,CAAC"}
1
+ {"version":3,"file":"tips.d.ts","sourceRoot":"","sources":["../../../src/tips/tips.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,MAAM,EAIN,MAAM,EACN,MAAM,EAGP,MAAM,QAAQ,CAAC;AAChB,OAAO,KAAK,OAAO,MAAM,iBAAiB,CAAC;AAQ3C,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAW9C,iEAAiE;AACjE,eAAO,MAAM,OAAO,gDAAwC,CAAC;AAC7D,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAQ1C;;+FAE+F;AAC/F,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;EAMnB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAE5C;sDACsD;AACtD,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;EAIhB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC;AAItC,uDAAuD;AACvD,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;QAQY,OAAO;;;QACR,OAAO;;;QACN,OAAO;;;;EAGrC,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAI1C,2FAA2F;AAC3F,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAElD,yCAAyC;AACzC,eAAO,MAAM,IAAI,WAAY,UAAU,KAAG,KAIxC,CAAC;AAIH,oDAAoD;AACpD,eAAO,MAAM,EAAE,UAAW,KAAK,KAAG,MAAkB,CAAC;AA8DrD;;;0CAG0C;AAC1C,eAAO,MAAM,OAAO;;;;;;iBAmBlB,CAAC;AAEH,sBAAsB;AACtB,eAAO,MAAM,OAAO;;;;iBAQlB,CAAC;AAEH,mCAAmC;AACnC,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAC;AAEH,iDAAiD;AACjD,eAAO,MAAM,gBAAgB;;;;;iBAK3B,CAAC;AAEH,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAC;AAyEH,6EAA6E;AAC7E,eAAO,MAAM,MAAM,UAAW,KAAK,WAAW,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmDjD,CAAC;AAIL,KAAK,aAAa,GACd,OAAO,OAAO,CAAC,gBAAgB,CAAC,IAAI,GACpC,OAAO,OAAO,CAAC,aAAa,CAAC,IAAI,GACjC,OAAO,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;AA0BnC;gEACgE;AAChE,eAAO,MAAM,kBAAkB,UACtB,MAAM,SACN,YAAY,KAClB,MAAM,CAAC,MAAM,CAAC,aAAa,CAkB7B,CAAC;AAUF;kCACkC;AAClC,eAAO,MAAM,eAAe,UAAW,UAAU,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAQtE,CAAC;AAEJ;;;0CAG0C;AAC1C,eAAO,MAAM,UAAU,UAAW,aAAa,WAAW,OAAO,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAUxF,CAAC;AAoEF;;;8BAG8B;AAC9B,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,aAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mGAC7B,CAAC;AAW1C;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAqBf,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
2
- import { Array, Duration, Effect, Match, Number, Option, Schema, Stream, pipe } from "effect";
2
+ import { Array, Duration, Effect, Equal, Match, Number, Option, Schema, Stream, pipe, } from "effect";
3
3
  import * as Command from "foldkit/command";
4
4
  import * as Dom from "foldkit/dom";
5
5
  import { defineMessageUnion } from "foldkit/message";
@@ -15,7 +15,9 @@ import { prefixKeys } from "../subscription/prefixKeys.js";
15
15
  const Side = Schema.Literals(["Top", "Right"]);
16
16
  /** What brought the tip: a resting pointer or keyboard focus. */
17
17
  export const Trigger = Schema.Literals(["Pointer", "Focus"]);
18
- const Words = { text: Schema.String, side: Side };
18
+ /** What a tip says: its control's name, and the key that does the same where the control
19
+ * names one (`data-tip-key`). */
20
+ const Words = { text: Schema.String, maybeKey: Schema.Option(Schema.String), side: Side };
19
21
  const ShownWords = { ...Words, trigger: Trigger };
20
22
  /** Where the page's tip is. `Cold`: none lately. `Waiting`: a pointer rests on a control, for
21
23
  * a moment first. `Shown`. `Leaving`: the pointer left, with a moment to cross onto the tip.
@@ -37,10 +39,10 @@ export const Model = Schema.Struct({
37
39
  // MESSAGE
38
40
  /** Union of all Messages the tip layer can produce. */
39
41
  export const Message = defineMessageUnion({
40
- EnteredTipTarget: { text: Schema.String, side: Side },
42
+ EnteredTipTarget: Words,
41
43
  LeftTipTarget: {},
42
44
  EnteredTip: {},
43
- FocusedTipTarget: { text: Schema.String, side: Side },
45
+ FocusedTipTarget: Words,
44
46
  BlurredTipTarget: {},
45
47
  PressedPointer: {},
46
48
  PressedKey: {},
@@ -70,6 +72,8 @@ const GRACE = Duration.millis(120);
70
72
  const WARMTH = Duration.millis(400);
71
73
  /** The attribute that holds a control's tip: its accessible name. */
72
74
  const TIP_ATTRIBUTE = "data-tip";
75
+ /** The attribute that holds the key a control's tip prints after its name. */
76
+ const TIP_KEY_ATTRIBUTE = "data-tip-key";
73
77
  const TIP_SELECTOR = `[${TIP_ATTRIBUTE}]`;
74
78
  const hasText = (element, text) => __foldkitBrandViewResult((element.isConnected && element.getAttribute(TIP_ATTRIBUTE) === text), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#hasText");
75
79
  /** The control a tip belongs to, found when it opens: the innermost hovered element with
@@ -117,9 +121,10 @@ export const WaitBeforeCooling = Command.define("WaitBeforeCooling", {
117
121
  messages: [Message.CompletedWaitBeforeCooling],
118
122
  execute: ({ version }) => __foldkitBrandViewResult((Effect.sleep(WARMTH).pipe(Effect.as(Message.CompletedWaitBeforeCooling({ version })))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#execute~5"),
119
123
  });
120
- const show = (model, text, side, trigger) => (__foldkitBrandViewResult(({
121
- model: modifyFields(model, { state: () => __foldkitBrandViewResult((TipState.Shown({ text, side, trigger })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#state") }),
122
- commands: [ShowTip({ tipId: model.id, text, trigger })],
124
+ const wordsOf = ({ text, maybeKey, side }) => (__foldkitBrandViewResult(({ text, maybeKey, side }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#wordsOf"));
125
+ const show = (model, words, trigger) => (__foldkitBrandViewResult(({
126
+ model: modifyFields(model, { state: () => __foldkitBrandViewResult((TipState.Shown({ ...wordsOf(words), trigger })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#state") }),
127
+ commands: [ShowTip({ tipId: model.id, text: words.text, trigger })],
123
128
  }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#show"));
124
129
  const hide = (model) => {
125
130
  const nextVersion = Number.increment(model.version);
@@ -128,12 +133,12 @@ const hide = (model) => {
128
133
  commands: [HideTip({ tipId: model.id }), WaitBeforeCooling({ version: nextVersion })],
129
134
  }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#hide");
130
135
  };
131
- const wait = (model, text, side) => {
136
+ const wait = (model, words) => {
132
137
  const nextVersion = Number.increment(model.version);
133
138
  return __foldkitBrandViewResult(({
134
139
  model: modifyFields(model, {
135
140
  version: () => __foldkitBrandViewResult((nextVersion), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#version~2"),
136
- state: () => __foldkitBrandViewResult((TipState.Waiting({ text, side })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#state~3"),
141
+ state: () => __foldkitBrandViewResult((TipState.Waiting(wordsOf(words))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#state~3"),
137
142
  }),
138
143
  commands: [WaitBeforeShowing({ version: nextVersion })],
139
144
  }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#wait");
@@ -143,7 +148,7 @@ const leave = (model, shown) => {
143
148
  return __foldkitBrandViewResult(({
144
149
  model: modifyFields(model, {
145
150
  version: () => __foldkitBrandViewResult((nextVersion), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#version~3"),
146
- state: () => __foldkitBrandViewResult((TipState.Leaving({ text: shown.text, side: shown.side, trigger: shown.trigger })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#state~4"),
151
+ state: () => __foldkitBrandViewResult((TipState.Leaving({ ...wordsOf(shown), trigger: shown.trigger })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#state~4"),
147
152
  }),
148
153
  commands: [WaitBeforeHiding({ version: nextVersion })],
149
154
  }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#leave");
@@ -159,47 +164,55 @@ const dismiss = (model) => __foldkitBrandViewResult((TipState.match(model.state,
159
164
  Leaving: () => __foldkitBrandViewResult((hide(model)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Leaving"),
160
165
  Warm: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Warm")),
161
166
  })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#dismiss");
162
- const entered = (model, text, side) => __foldkitBrandViewResult((TipState.match(model.state, {
163
- Cold: () => __foldkitBrandViewResult((wait(model, text, side)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Cold~2"),
164
- Waiting: (waiting) => (__foldkitBrandViewResult((waiting.text === text ? { model } : wait(model, text, side)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Waiting~2")),
165
- Shown: (shown) => (__foldkitBrandViewResult((shown.text === text ? { model } : show(model, text, side, "Pointer")), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Shown~2")),
166
- Leaving: (leaving) => __foldkitBrandViewResult((leaving.text === text
167
+ const entered = (model, words) => __foldkitBrandViewResult((TipState.match(model.state, {
168
+ Cold: () => __foldkitBrandViewResult((wait(model, words)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Cold~2"),
169
+ Waiting: (waiting) => (__foldkitBrandViewResult((waiting.text === words.text ? { model } : wait(model, words)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Waiting~2")),
170
+ Shown: (shown) => (__foldkitBrandViewResult((shown.text === words.text ? { model } : show(model, words, "Pointer")), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Shown~2")),
171
+ Leaving: (leaving) => __foldkitBrandViewResult((leaving.text === words.text
167
172
  ? {
168
173
  model: modifyFields(model, {
169
- state: () => __foldkitBrandViewResult((TipState.Shown({ text, side, trigger: leaving.trigger })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#state~6"),
174
+ state: () => __foldkitBrandViewResult((TipState.Shown({ ...wordsOf(words), trigger: leaving.trigger })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#state~6"),
170
175
  }),
171
176
  }
172
- : show(model, text, side, "Pointer")), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Leaving~2"),
173
- Warm: () => __foldkitBrandViewResult((show(model, text, side, "Pointer")), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Warm~2"),
177
+ : show(model, words, "Pointer")), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Leaving~2"),
178
+ Warm: () => __foldkitBrandViewResult((show(model, words, "Pointer")), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Warm~2"),
174
179
  })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#entered");
175
180
  /** Processes a tip layer Message and returns the next Model and Commands. */
176
181
  export const update = (model, message) => __foldkitBrandViewResult((Message.match(message, {
177
- EnteredTipTarget: ({ text, side }) => __foldkitBrandViewResult((entered(model, text, side)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#EnteredTipTarget"),
182
+ EnteredTipTarget: (words) => __foldkitBrandViewResult((entered(model, wordsOf(words))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#EnteredTipTarget"),
178
183
  LeftTipTarget: () => __foldkitBrandViewResult((TipState.matchOrElse(model.state, {
179
184
  Waiting: () => __foldkitBrandViewResult((cool(model)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Waiting~3"),
180
185
  Shown: (shown) => (__foldkitBrandViewResult((shown.trigger === "Pointer" ? leave(model, shown) : { model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Shown~3")),
181
186
  }, () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~11")))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#LeftTipTarget"),
182
187
  EnteredTip: () => __foldkitBrandViewResult((TipState.matchOrElse(model.state, {
183
- Leaving: ({ text, side, trigger }) => (__foldkitBrandViewResult(({
184
- model: modifyFields(model, { state: () => __foldkitBrandViewResult((TipState.Shown({ text, side, trigger })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#state~7") }),
188
+ Leaving: (leaving) => (__foldkitBrandViewResult(({
189
+ model: modifyFields(model, {
190
+ state: () => __foldkitBrandViewResult((TipState.Shown({ ...wordsOf(leaving), trigger: leaving.trigger })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#state~7"),
191
+ }),
185
192
  }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Leaving~3")),
186
193
  }, () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~12")))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#EnteredTip"),
187
- FocusedTipTarget: ({ text, side }) => __foldkitBrandViewResult((show(model, text, side, "Focus")), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#FocusedTipTarget"),
194
+ FocusedTipTarget: (words) => __foldkitBrandViewResult((show(model, wordsOf(words), "Focus")), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#FocusedTipTarget"),
188
195
  BlurredTipTarget: () => __foldkitBrandViewResult((dismiss(model)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#BlurredTipTarget"),
189
196
  PressedPointer: () => __foldkitBrandViewResult((dismiss(model)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#PressedPointer"),
190
197
  PressedKey: () => __foldkitBrandViewResult((dismiss(model)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#PressedKey"),
191
198
  CompletedWaitBeforeShowing: ({ version }) => __foldkitBrandViewResult((TipState.matchOrElse(model.state, {
192
- Waiting: ({ text, side }) => __foldkitBrandViewResult((model.version === version ? show(model, text, side, "Pointer") : { model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Waiting~4"),
199
+ Waiting: (waiting) => __foldkitBrandViewResult((model.version === version ? show(model, wordsOf(waiting), "Pointer") : { model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Waiting~4"),
193
200
  }, () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~13")))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#CompletedWaitBeforeShowing"),
194
201
  CompletedWaitBeforeHiding: ({ version }) => __foldkitBrandViewResult((TipState.matchOrElse(model.state, { Leaving: () => (__foldkitBrandViewResult((model.version === version ? hide(model) : { model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Leaving~4")) }, () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~14")))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#CompletedWaitBeforeHiding"),
195
202
  CompletedWaitBeforeCooling: ({ version }) => __foldkitBrandViewResult((TipState.matchOrElse(model.state, { Warm: () => (__foldkitBrandViewResult((model.version === version ? cool(model) : { model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#Warm~3")) }, () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~15")))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#CompletedWaitBeforeCooling"),
196
203
  CompletedShowTip: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#CompletedShowTip")),
197
204
  CompletedHideTip: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#CompletedHideTip")),
198
205
  })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#update");
199
- const sideOf = (element) => __foldkitBrandViewResult((element.closest('[aria-orientation="vertical"]') === null ? "Top" : "Right"), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#sideOf");
206
+ /** A control in a vertical strip of tools, or set in vertical writing (a folded rail's title
207
+ * bar), takes its tip at its side, where the tip would cover the next tool or the top bar. */
208
+ const sideOf = (element) => __foldkitBrandViewResult((element.closest('[aria-orientation="vertical"]') === null &&
209
+ !getComputedStyle(element).writingMode.startsWith("vertical")
210
+ ? "Top"
211
+ : "Right"), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#sideOf");
200
212
  const tipTargetOf = (target) => __foldkitBrandViewResult((pipe(Option.liftPredicate(target, (node) => __foldkitBrandViewResult((node instanceof Element), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~16")), Option.flatMap((element) => __foldkitBrandViewResult((Option.fromNullishOr(element.closest(TIP_SELECTOR))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~17")), Option.flatMap((element) => __foldkitBrandViewResult((Option.map(Option.fromNullishOr(element.getAttribute(TIP_ATTRIBUTE)), (text) => (__foldkitBrandViewResult(({
201
213
  element,
202
214
  text,
215
+ maybeKey: Option.fromNullishOr(element.getAttribute(TIP_KEY_ATTRIBUTE)),
203
216
  side: sideOf(element),
204
217
  }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~19")))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~18")))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#tipTargetOf");
205
218
  /** What a pointer arriving over `target` means for the tip: over a control with a tip, over
@@ -214,12 +227,12 @@ export const pointerOverMessage = (tipId, event) => {
214
227
  }
215
228
  return __foldkitBrandViewResult((Option.some(Option.match(tipTargetOf(event.target), {
216
229
  onNone: () => __foldkitBrandViewResult((Message.LeftTipTarget()), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#onNone"),
217
- onSome: ({ text, side }) => __foldkitBrandViewResult((Message.EnteredTipTarget({ text, side })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#onSome"),
230
+ onSome: (target) => __foldkitBrandViewResult((Message.EnteredTipTarget(wordsOf(target))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#onSome"),
218
231
  }))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#pointerOverMessage");
219
232
  };
220
233
  /** What focus arriving on `target` means for the tip: keyboard focus (`:focus-visible`) on a
221
234
  * control with a tip shows it; any other focus says nothing. */
222
- const focusInMessage = (event) => __foldkitBrandViewResult((Option.map(Option.filter(tipTargetOf(event.target), ({ element }) => __foldkitBrandViewResult((element.matches(":focus-visible")), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~20")), ({ text, side }) => __foldkitBrandViewResult((Message.FocusedTipTarget({ text, side })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~21"))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#focusInMessage");
235
+ const focusInMessage = (event) => __foldkitBrandViewResult((Option.map(Option.filter(tipTargetOf(event.target), ({ element }) => __foldkitBrandViewResult((element.matches(":focus-visible")), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~20")), (target) => __foldkitBrandViewResult((Message.FocusedTipTarget(wordsOf(target))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~21"))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#focusInMessage");
223
236
  /** Focus leaving a control with a tip, not into a part of itself, closes the tip (and
224
237
  * cancels one still waiting). */
225
238
  export const focusOutMessage = (event) => __foldkitBrandViewResult((Option.map(Option.filter(tipTargetOf(event.target), ({ element }) => __foldkitBrandViewResult((!(event.relatedTarget instanceof Node && element.contains(event.relatedTarget))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~22")), () => __foldkitBrandViewResult((Message.BlurredTipTarget()), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~23"))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#focusOutMessage");
@@ -244,7 +257,9 @@ const pointerOverChanges = (tipId) => __foldkitBrandViewResult((Dom.streamFromEv
244
257
  }).pipe(Stream.changesWith((previous, next) => __foldkitBrandViewResult((previous._tag === next._tag && sameWords(previous, next)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~24")))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#pointerOverChanges");
245
258
  const sameWords = (previous, next) => __foldkitBrandViewResult((previous._tag !== "EnteredTipTarget" ||
246
259
  next._tag !== "EnteredTipTarget" ||
247
- (previous.text === next.text && previous.side === next.side)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#sameWords");
260
+ (previous.text === next.text &&
261
+ previous.side === next.side &&
262
+ Equal.equals(previous.maybeKey, next.maybeKey))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#sameWords");
248
263
  const tipSubscriptions = Subscription.make()((entry) => (__foldkitBrandViewResult(({
249
264
  pointerAndFocus: entry({ tipId: Schema.String }, {
250
265
  modelToDependencies: (model) => (__foldkitBrandViewResult(({ tipId: model.id }), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#modelToDependencies")),
@@ -286,10 +301,11 @@ const tipSubscriptions = Subscription.make()((entry) => (__foldkitBrandViewResul
286
301
  * only while a tip shows. */
287
302
  export const subscriptions = (keyPrefix) => __foldkitBrandViewResult((prefixKeys(keyPrefix, tipSubscriptions)), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#subscriptions");
288
303
  // VIEW
289
- const wordsOf = (model) => __foldkitBrandViewResult((Match.value(model.state).pipe(Match.withReturnType(), Match.tag("Waiting", "Shown", "Leaving", ({ text, side }) => __foldkitBrandViewResult((Option.some({ text, side })), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~26")), Match.orElse(() => __foldkitBrandViewResult((Option.none()), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~27")))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#wordsOf");
304
+ const maybeWordsOf = (model) => __foldkitBrandViewResult((Match.value(model.state).pipe(Match.withReturnType(), Match.tag("Waiting", "Shown", "Leaving", (words) => __foldkitBrandViewResult((Option.some(wordsOf(words))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~26")), Match.orElse(() => __foldkitBrandViewResult((Option.none()), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~27")))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#maybeWordsOf");
290
305
  /**
291
306
  * The page's one tip: `div.tip[popover="manual"][aria-hidden="true"]`, which shows the words
292
- * of any control carrying `data-tip` (IconButton, the glyph switch, a board's grip). Render it
307
+ * of any control carrying `data-tip` (IconButton, the glyph switch, a board's grip), then the
308
+ * key in its `data-tip-key` where it has one (an IconButton's `keyHint`). Render it
293
309
  * once, beside the Announcer, and lift `subscriptions(keyPrefix)`. It opens on a resting
294
310
  * pointer after a moment (at once while another has just closed), never on touch, and on
295
311
  * keyboard focus; it can be hovered; leaving, blur, a press or any key closes it, and Escape
@@ -301,7 +317,7 @@ const wordsOf = (model) => __foldkitBrandViewResult((Match.value(model.state).pi
301
317
  * accessibility tree, and any control takes one by carrying `data-tip`.
302
318
  */
303
319
  export const view = defineView((model, h) => {
304
- const maybeWords = wordsOf(model);
320
+ const maybeWords = maybeWordsOf(model);
305
321
  return __foldkitBrandViewResult((h.div([
306
322
  h.Id(model.id),
307
323
  h.Popover("manual"),
@@ -310,5 +326,11 @@ export const view = defineView((model, h) => {
310
326
  ...(Option.exists(maybeWords, ({ side }) => __foldkitBrandViewResult((side === "Right"), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~29"))
311
327
  ? [h.DataAttribute("side", "right")]
312
328
  : []),
313
- ], Option.isSome(maybeWords) ? [maybeWords.value.text] : [])), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~28");
329
+ ], Option.match(maybeWords, {
330
+ onNone: () => __foldkitBrandViewResult(([]), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#onNone~3"),
331
+ onSome: ({ text, maybeKey }) => __foldkitBrandViewResult(([
332
+ text,
333
+ ...Array.fromOption(Option.map(maybeKey, (key) => __foldkitBrandViewResult((h.kbd([h.Class("key-hint")], [key])), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~30"))),
334
+ ]), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#onSome~3"),
335
+ }))), "@gjermundgaraba-designs/pixel-paper/src/tips/tips.js#anonymous~28");
314
336
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjermundgaraba-designs/pixel-paper",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -2,8 +2,10 @@ import { Button as UiButton } from "@foldkit/ui";
2
2
  import type { Html, HtmlBuilder } from "foldkit/html";
3
3
 
4
4
  import type { Attributes } from "../attributes/attributes.ts";
5
+ import type { KeyHintConfig } from "../button/button.ts";
5
6
  import * as Glyph from "../glyph/glyph.ts";
6
7
  import type { GlyphName } from "../glyph/glyphs.ts";
8
+ import { keyLabel } from "../keyHint/keyLabel.ts";
7
9
 
8
10
  // VIEW
9
11
 
@@ -17,13 +19,25 @@ export type ViewConfig<Message> = Readonly<{
17
19
  isQuiet?: boolean;
18
20
  /** Unavailable but focusable (`aria-disabled`), as in `@foldkit/ui`. */
19
21
  isDisabled?: boolean;
22
+ /** The key that does the same, printed in the tip after the label (`data-tip-key`): the
23
+ * button has no words to print it beside. */
24
+ keyHint?: KeyHintConfig;
20
25
  /** Further attributes for the `<button>`, never a class. */
21
26
  attributes?: Attributes<Message>;
22
27
  }>;
23
28
 
24
- /** A bare glyph button, 24px square. Its label is its accessible name and its tip. */
29
+ /** A bare glyph button, 24px square. Its label is its accessible name and its tip, and its
30
+ * key, where it has one, prints in the tip after the label. */
25
31
  export const view = <Message>(config: ViewConfig<Message>, h: HtmlBuilder<Message>): Html => {
26
- const { glyph, label, onClick, isQuiet = false, isDisabled = false, attributes = [] } = config;
32
+ const {
33
+ glyph,
34
+ label,
35
+ onClick,
36
+ isQuiet = false,
37
+ isDisabled = false,
38
+ keyHint,
39
+ attributes = [],
40
+ } = config;
27
41
 
28
42
  return UiButton.view(
29
43
  {
@@ -36,6 +50,9 @@ export const view = <Message>(config: ViewConfig<Message>, h: HtmlBuilder<Messag
36
50
  ...groups.button,
37
51
  h.AriaLabel(label),
38
52
  h.DataAttribute("tip", label),
53
+ ...(keyHint === undefined
54
+ ? []
55
+ : [h.DataAttribute("tip-key", keyLabel(keyHint.keys, keyHint.platform))]),
39
56
  h.Class(isQuiet ? "iconbtn quiet" : "iconbtn"),
40
57
  ],
41
58
  [Glyph.view({ name: glyph }, h)],
@@ -26,6 +26,9 @@ export type ViewConfig = Readonly<{
26
26
  /** Small Buttons and IconButtons at the title bar's right end, after the meta. On the ink
27
27
  * bar they print in paper, flat, and ring in their own colour inside their edge. */
28
28
  actions?: ReadonlyArray<Html | string>;
29
+ /** A strip on the head tint between the title bar and the body, for a field that picks what
30
+ * the body shows (an area over its tasks). Its labels print in on-head. */
31
+ shelf?: ReadonlyArray<Html | string>;
29
32
  body: ReadonlyArray<Html | string>;
30
33
  /** A ruled foot under the body: a note line, then actions pushed right. */
31
34
  foot?: ReadonlyArray<Html | string>;
@@ -46,6 +49,7 @@ export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html
46
49
  lead = [],
47
50
  meta,
48
51
  actions = [],
52
+ shelf = [],
49
53
  body,
50
54
  foot = [],
51
55
  isFlush = false,
@@ -68,6 +72,7 @@ export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html
68
72
  ...slotView("span", "panel-acts", actions, h),
69
73
  ],
70
74
  ),
75
+ ...slotView("div", "panel-shelf", shelf, h),
71
76
  ...(isFlush ? body : [h.div([h.Class("panel-body")], body)]),
72
77
  ...slotView("div", "panel-foot", foot, h),
73
78
  ],