@gjermundgaraba-designs/pixel-paper 0.23.0 → 0.24.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.
- package/DESIGN.md +8 -7
- package/dist/src/iconButton/iconButton.d.ts +6 -1
- package/dist/src/iconButton/iconButton.d.ts.map +1 -1
- package/dist/src/iconButton/iconButton.js +7 -2
- package/dist/src/panel/panel.d.ts +3 -0
- package/dist/src/panel/panel.d.ts.map +1 -1
- package/dist/src/panel/panel.js +2 -1
- package/dist/src/rail/index.d.ts +1 -1
- package/dist/src/rail/index.d.ts.map +1 -1
- package/dist/src/rail/index.js +1 -1
- package/dist/src/rail/rail.d.ts +78 -17
- package/dist/src/rail/rail.d.ts.map +1 -1
- package/dist/src/rail/rail.js +98 -41
- package/dist/src/tips/tips.d.ts +30 -1
- package/dist/src/tips/tips.d.ts.map +1 -1
- package/dist/src/tips/tips.js +53 -31
- package/package.json +1 -1
- package/src/iconButton/iconButton.ts +19 -2
- package/src/panel/panel.ts +5 -0
- package/src/pixel-paper.css +119 -4
- package/src/rail/index.ts +2 -0
- package/src/rail/rail.ts +119 -41
- package/src/tips/tips.ts +71 -35
- package/src/tokens.css +1 -0
package/dist/src/tips/tips.js
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
42
|
+
EnteredTipTarget: Words,
|
|
41
43
|
LeftTipTarget: {},
|
|
42
44
|
EnteredTip: {},
|
|
43
|
-
FocusedTipTarget:
|
|
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
|
|
121
|
-
|
|
122
|
-
|
|
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,
|
|
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(
|
|
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({
|
|
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,
|
|
163
|
-
Cold: () => __foldkitBrandViewResult((wait(model,
|
|
164
|
-
Waiting: (waiting) => (__foldkitBrandViewResult((waiting.text === text ? { model } : wait(model,
|
|
165
|
-
Shown: (shown) => (__foldkitBrandViewResult((shown.text === text ? { model } : show(model,
|
|
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({
|
|
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,
|
|
173
|
-
Warm: () => __foldkitBrandViewResult((show(model,
|
|
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: (
|
|
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: (
|
|
184
|
-
model: modifyFields(model, {
|
|
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: (
|
|
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: (
|
|
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
|
-
|
|
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: (
|
|
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")), (
|
|
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 &&
|
|
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
|
|
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)
|
|
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 =
|
|
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.
|
|
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
|
@@ -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 {
|
|
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)],
|
package/src/panel/panel.ts
CHANGED
|
@@ -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
|
],
|
package/src/pixel-paper.css
CHANGED
|
@@ -979,6 +979,19 @@ a.textbtn {
|
|
|
979
979
|
flex: 1 1 100%;
|
|
980
980
|
}
|
|
981
981
|
|
|
982
|
+
/* the shelf: a strip on the head tint under the bar, for a field that picks what the body
|
|
983
|
+
shows */
|
|
984
|
+
.panel-shelf {
|
|
985
|
+
padding: 10px 12px 12px;
|
|
986
|
+
background: var(--head);
|
|
987
|
+
border-bottom: var(--hair) solid var(--rule);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/* on the head tint a field's label takes on-head: muted misses AA there */
|
|
991
|
+
.panel-shelf :is(.field > label:not(.choice), .field-label) {
|
|
992
|
+
color: var(--on-head);
|
|
993
|
+
}
|
|
994
|
+
|
|
982
995
|
/* ---------- the panel's title-bar lead and actions ---------- */
|
|
983
996
|
|
|
984
997
|
/* small buttons and icon buttons at the bar's right end, and an icon button before the
|
|
@@ -4828,6 +4841,16 @@ dialog.veil:focus {
|
|
|
4828
4841
|
overflow: visible;
|
|
4829
4842
|
}
|
|
4830
4843
|
|
|
4844
|
+
/* the key that does the same as the control, after its words, as a button prints it */
|
|
4845
|
+
.tip .key-hint {
|
|
4846
|
+
margin-left: 6px;
|
|
4847
|
+
font-family: var(--font-px);
|
|
4848
|
+
font-size: var(--type-label);
|
|
4849
|
+
text-transform: none;
|
|
4850
|
+
letter-spacing: 0;
|
|
4851
|
+
opacity: var(--fade-on-ink);
|
|
4852
|
+
}
|
|
4853
|
+
|
|
4831
4854
|
/* ---------- toolbars: one strip of tools, one stop for Tab ---------- */
|
|
4832
4855
|
|
|
4833
4856
|
/* the rules between tools are the strip's ink showing through a 2px gap */
|
|
@@ -5022,8 +5045,7 @@ dialog.veil:focus {
|
|
|
5022
5045
|
/* ---------- the rail: a column of panels beside the deck, a drawer on a phone ---------- */
|
|
5023
5046
|
|
|
5024
5047
|
/* one element in two places: at rest, fixed down the left edge from the top bar's lower
|
|
5025
|
-
edge (as the stage hangs)
|
|
5026
|
-
popover drawer. The reader's width, when they gave one, is --railed-width, which the view
|
|
5048
|
+
edge (as the stage hangs); on a phone the same element opens as a popover drawer. The reader's width, when they gave one, is --railed-width, which the view
|
|
5027
5049
|
sets on .railed, so the page's padding and the rail's width follow it together; without one,
|
|
5028
5050
|
--rail. It is not inherited: each box it sizes takes it from .railed by name, so a width that
|
|
5029
5051
|
changes under a dragged splitter restyles those four and none of the rows inside them */
|
|
@@ -5046,8 +5068,8 @@ dialog.veil:focus {
|
|
|
5046
5068
|
top: anchor(--topbar bottom, 0px);
|
|
5047
5069
|
bottom: var(--statusbar-h);
|
|
5048
5070
|
left: 0;
|
|
5049
|
-
display:
|
|
5050
|
-
|
|
5071
|
+
display: flex;
|
|
5072
|
+
flex-direction: column;
|
|
5051
5073
|
gap: var(--gap);
|
|
5052
5074
|
width: var(--railed-width, var(--rail));
|
|
5053
5075
|
height: auto;
|
|
@@ -5059,9 +5081,45 @@ dialog.veil:focus {
|
|
|
5059
5081
|
}
|
|
5060
5082
|
|
|
5061
5083
|
.rail > * {
|
|
5084
|
+
flex: none;
|
|
5062
5085
|
min-width: 0;
|
|
5063
5086
|
}
|
|
5064
5087
|
|
|
5088
|
+
/* the rail's top stays put: what comes before its last panel keeps its height, and the last
|
|
5089
|
+
panel takes the room left, at most its own height, its bar, shelf and foot staying while
|
|
5090
|
+
its body scrolls (the padded body, or a flush body's list). The rail scrolls as a whole
|
|
5091
|
+
only when what comes before that panel is taller than the column */
|
|
5092
|
+
.rail > .panel:last-child {
|
|
5093
|
+
display: flex;
|
|
5094
|
+
flex-direction: column;
|
|
5095
|
+
flex: 0 1 auto;
|
|
5096
|
+
min-height: 0;
|
|
5097
|
+
}
|
|
5098
|
+
|
|
5099
|
+
.rail > .panel:last-child > :is(.panel-tb, .panel-shelf, .panel-foot) {
|
|
5100
|
+
flex: none;
|
|
5101
|
+
}
|
|
5102
|
+
|
|
5103
|
+
.rail > .panel:last-child > :not(.panel-tb, .panel-shelf, .panel-foot) {
|
|
5104
|
+
flex: 0 1 auto;
|
|
5105
|
+
min-height: 0;
|
|
5106
|
+
overflow-y: auto;
|
|
5107
|
+
}
|
|
5108
|
+
|
|
5109
|
+
/* a long name ends short of the bar's tools rather than wrapping the bar taller */
|
|
5110
|
+
.rail .panel-tb h2 {
|
|
5111
|
+
min-width: 0;
|
|
5112
|
+
overflow: hidden;
|
|
5113
|
+
overflow-wrap: normal;
|
|
5114
|
+
white-space: nowrap;
|
|
5115
|
+
text-overflow: ellipsis;
|
|
5116
|
+
}
|
|
5117
|
+
|
|
5118
|
+
/* the toggle is the button itself in the bar's lead */
|
|
5119
|
+
.rail-toggle {
|
|
5120
|
+
display: contents;
|
|
5121
|
+
}
|
|
5122
|
+
|
|
5065
5123
|
/* the splitter on the rail's right edge: a 12px strip from the edge outward, over the page's
|
|
5066
5124
|
margin and never over the rail's own scrollbar, and beside the rail rather than in it (the
|
|
5067
5125
|
rail scrolls, and its drawer's first Tab stop is its first control). It shows itself as a
|
|
@@ -5102,6 +5160,48 @@ dialog.veil:focus {
|
|
|
5102
5160
|
display: none;
|
|
5103
5161
|
}
|
|
5104
5162
|
|
|
5163
|
+
/* folded: the first panel stands its title bar on end on the ground where the rail stood, the
|
|
5164
|
+
toggle at its top and the title and count reading down; the rest of the rail and the
|
|
5165
|
+
splitter are gone, and the deck takes the width. Above 900px only: the drawer opens whole.
|
|
5166
|
+
Folding and unfolding move in three steps, as the drawer enters, but never while a pointer
|
|
5167
|
+
drags the splitter, where the rail follows the pointer */
|
|
5168
|
+
@media not (max-width: 900px) {
|
|
5169
|
+
.railed.folded {
|
|
5170
|
+
--railed-width: var(--rail-folded);
|
|
5171
|
+
}
|
|
5172
|
+
|
|
5173
|
+
.railed.folded > .rail > :not(.rail-head + .panel),
|
|
5174
|
+
.railed.folded > .rail > .rail-head + .panel > :not(.panel-tb),
|
|
5175
|
+
.railed.folded > .rail > .rail-head + .panel > .panel-tb > .panel-acts {
|
|
5176
|
+
display: none;
|
|
5177
|
+
}
|
|
5178
|
+
|
|
5179
|
+
.railed.folded > .rail > .rail-head + .panel {
|
|
5180
|
+
align-self: flex-start;
|
|
5181
|
+
}
|
|
5182
|
+
|
|
5183
|
+
.railed.folded > .rail > .rail-head + .panel > .panel-tb {
|
|
5184
|
+
writing-mode: vertical-rl;
|
|
5185
|
+
min-width: 31px;
|
|
5186
|
+
min-height: 0;
|
|
5187
|
+
padding: 3px 3px 12px;
|
|
5188
|
+
}
|
|
5189
|
+
|
|
5190
|
+
.railed.folded > .rail > .rail-head + .panel > .panel-tb > .meta {
|
|
5191
|
+
margin: 0;
|
|
5192
|
+
}
|
|
5193
|
+
|
|
5194
|
+
.railed,
|
|
5195
|
+
.railed > :is(.rail, .bench) {
|
|
5196
|
+
transition: var(--motion-card) steps(3, end);
|
|
5197
|
+
transition-property: padding-left, width, left;
|
|
5198
|
+
}
|
|
5199
|
+
|
|
5200
|
+
:root[data-pixel-paper-dragging] :is(.railed, .railed > .rail, .railed > .bench) {
|
|
5201
|
+
transition: none;
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
|
|
5105
5205
|
@media (max-width: 900px) {
|
|
5106
5206
|
.railed {
|
|
5107
5207
|
padding-left: 0;
|
|
@@ -5140,6 +5240,16 @@ dialog.veil:focus {
|
|
|
5140
5240
|
display: inline-flex;
|
|
5141
5241
|
justify-self: start;
|
|
5142
5242
|
}
|
|
5243
|
+
|
|
5244
|
+
/* the drawer opens and closes but never folds, so it prints no toggle, and the bar the
|
|
5245
|
+
toggle led keeps its own padding */
|
|
5246
|
+
.panel-lead:has(> .rail-toggle) {
|
|
5247
|
+
display: none;
|
|
5248
|
+
}
|
|
5249
|
+
|
|
5250
|
+
.panel-tb:has(> .panel-lead > .rail-toggle) {
|
|
5251
|
+
padding-left: 12px;
|
|
5252
|
+
}
|
|
5143
5253
|
}
|
|
5144
5254
|
|
|
5145
5255
|
@keyframes railIn {
|
|
@@ -5842,4 +5952,9 @@ dialog.veil:focus {
|
|
|
5842
5952
|
.toasts .toast {
|
|
5843
5953
|
animation: none;
|
|
5844
5954
|
}
|
|
5955
|
+
|
|
5956
|
+
.railed,
|
|
5957
|
+
.railed > :is(.rail, .bench) {
|
|
5958
|
+
transition: none;
|
|
5959
|
+
}
|
|
5845
5960
|
}
|