@0001-labs/design-system-1 0.3.0-alpha.21 → 0.3.0-alpha.23
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/DS1/0-face/bento.ts +13 -4
- package/DS1/0-face/calendar.ts +23 -0
- package/DS1/1-root/bento.css +25 -12
- package/DS1/1-root/cjk.css +1 -1
- package/DS1/1-root/controls.css +22 -32
- package/DS1/1-root/fonts.css +1 -1
- package/DS1/1-root/one.css +1 -1
- package/DS1/1-root/one.semantic.css +1 -1
- package/DS1/2-core/ds-switch.ts +2 -2
- package/DS1/2-core/styles/ds-checkbox.css +11 -7
- package/DS1/2-core/styles/ds-radio.css +5 -2
- package/DS1/2-core/styles/ds-switch.css +7 -23
- package/DS1/3-unit/ds-bento.ts +8 -1
- package/DS1/3-unit/ds-calendar.ts +21 -4
- package/DS1/3-unit/styles/ds-bento.css +16 -10
- package/DS1/3-unit/styles/ds-calendar.css +30 -8
- package/dist/0-face/bento.d.ts +6 -2
- package/dist/0-face/bento.d.ts.map +1 -1
- package/dist/0-face/bento.js +12 -4
- package/dist/0-face/calendar.d.ts +11 -0
- package/dist/0-face/calendar.d.ts.map +1 -1
- package/dist/0-face/calendar.js +19 -0
- package/dist/2-core/ds-switch.d.ts +2 -2
- package/dist/2-core/ds-switch.d.ts.map +1 -1
- package/dist/2-core/ds-switch.js +2 -2
- package/dist/3-unit/ds-bento.d.ts +12 -0
- package/dist/3-unit/ds-bento.d.ts.map +1 -1
- package/dist/3-unit/ds-bento.js +4 -1
- package/dist/3-unit/ds-calendar.d.ts +17 -0
- package/dist/3-unit/ds-calendar.d.ts.map +1 -1
- package/dist/3-unit/ds-calendar.js +18 -5
- package/dist/design-system-1.bundle.js +66 -15
- package/dist/design-system-1.bundle.js.map +1 -1
- package/dist/design-system-1.bundle.min.js +903 -872
- package/dist/design-system-1.bundle.min.js.map +1 -1
- package/package.json +1 -1
package/DS1/0-face/bento.ts
CHANGED
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
* A tile's `data-place` is its square across, its square down, its width and
|
|
20
20
|
* its height, in whole squares counted from 0. Tiles are placed a square
|
|
21
21
|
* apart; a tile a square away from another, with the two overlapping along
|
|
22
|
-
* that side, reaches towards it.
|
|
22
|
+
* that side, reaches towards it. A tile can have a place per layout,
|
|
23
|
+
* `data-place-phone="0 0 12 7"`, taken while the bento says
|
|
24
|
+
* `data-layout="phone"`; the page sets the layout and the columns for it.
|
|
23
25
|
*/
|
|
24
26
|
|
|
25
27
|
/** A tile's place in squares: across, down, wide, high. */
|
|
@@ -51,10 +53,17 @@ export function bentoReach(places: readonly BentoPlace[]): BentoReach[] {
|
|
|
51
53
|
});
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
/**
|
|
56
|
+
/** A tile's place in the bento's layout: `data-place-<layout>` where the bento names one and the tile has it, else `data-place`. */
|
|
57
|
+
export function tilePlace(tile: HTMLElement, layout = ""): BentoPlace | undefined {
|
|
58
|
+
const own = layout ? tile.getAttribute(`data-place-${layout}`) : null;
|
|
59
|
+
return bentoPlace(own ?? tile.dataset.place);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Places one squares bento's tiles: their squares and their reach, as custom properties. The bento's `data-layout` (or `layout`) picks each tile's place for that layout. */
|
|
55
63
|
export function placeBento(bento: HTMLElement): void {
|
|
64
|
+
const layout = bento.dataset.layout ?? bento.getAttribute("layout") ?? "";
|
|
56
65
|
const tiles = [...bento.children].filter((child): child is HTMLElement => child instanceof HTMLElement && !child.hidden);
|
|
57
|
-
const placed = tiles.map(tile => ({ tile, place:
|
|
66
|
+
const placed = tiles.map(tile => ({ tile, place: tilePlace(tile, layout) })).filter((entry): entry is { tile: HTMLElement; place: BentoPlace } => !!entry.place);
|
|
58
67
|
const reach = bentoReach(placed.map(entry => entry.place));
|
|
59
68
|
placed.forEach(({ tile, place: [x, y, w, h] }, index) => {
|
|
60
69
|
const sides = reach[index]!;
|
|
@@ -77,7 +86,7 @@ export function placeBentos(root: ParentNode = document): () => void {
|
|
|
77
86
|
});
|
|
78
87
|
for (const bento of bentos) {
|
|
79
88
|
placeBento(bento);
|
|
80
|
-
observer.observe(bento, { childList: true, subtree: false, attributes: true, attributeFilter: ["data-place", "hidden"] });
|
|
89
|
+
observer.observe(bento, { childList: true, subtree: false, attributes: true, attributeFilter: ["data-place", "data-layout", "hidden"] });
|
|
81
90
|
for (const tile of bento.children) observer.observe(tile, { attributes: true, attributeFilter: ["data-place", "hidden"] });
|
|
82
91
|
}
|
|
83
92
|
return () => observer.disconnect();
|
package/DS1/0-face/calendar.ts
CHANGED
|
@@ -173,8 +173,31 @@ export type CalendarEvent = {
|
|
|
173
173
|
time?: string;
|
|
174
174
|
/** A colour token or value for the event's fill (`var(--ds-zenith-blue)`). */
|
|
175
175
|
color?: string;
|
|
176
|
+
/** How much it counts for in a calendar's heat (commits, purchases, hours); 1 when left out. */
|
|
177
|
+
count?: number;
|
|
176
178
|
};
|
|
177
179
|
|
|
180
|
+
/** A day's heat: 1 (a little) to 4 (the most). */
|
|
181
|
+
export type HeatLevel = 1 | 2 | 3 | 4;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* How strongly each day is lit, as a contribution graph lights it: the
|
|
185
|
+
* amounts of its events added up, in four steps of the busiest day (or of
|
|
186
|
+
* `max`, for a scale that stays put from month to month). A day with nothing
|
|
187
|
+
* has no level.
|
|
188
|
+
*/
|
|
189
|
+
export function heatLevels(byDay: ReadonlyMap<Day, readonly CalendarEvent[]>, max?: number): Map<Day, HeatLevel> {
|
|
190
|
+
const amounts = new Map<Day, number>();
|
|
191
|
+
for (const [day, events] of byDay) {
|
|
192
|
+
const amount = events.reduce((sum, event) => sum + (typeof event.count === "number" && Number.isFinite(event.count) ? Math.max(0, event.count) : 1), 0);
|
|
193
|
+
if (amount > 0) amounts.set(day, amount);
|
|
194
|
+
}
|
|
195
|
+
const top = typeof max === "number" && Number.isFinite(max) && max > 0 ? max : Math.max(0, ...amounts.values());
|
|
196
|
+
const levels = new Map<Day, HeatLevel>();
|
|
197
|
+
for (const [day, amount] of amounts) levels.set(day, Math.min(4, Math.max(1, Math.ceil((amount / top) * 4))) as HeatLevel);
|
|
198
|
+
return levels;
|
|
199
|
+
}
|
|
200
|
+
|
|
178
201
|
/** The events that touch each day between `from` and `to`, in order: longest first, then by time. */
|
|
179
202
|
export function eventsByDay(events: readonly CalendarEvent[], from: Day, to: Day): Map<Day, CalendarEvent[]> {
|
|
180
203
|
const days = new Map<Day, CalendarEvent[]>();
|
package/DS1/1-root/bento.css
CHANGED
|
@@ -24,16 +24,24 @@
|
|
|
24
24
|
* data-rows="2" a tile two rows high (to 6)
|
|
25
25
|
* data-place="0 0 15 7" a tile's square, width and height in squares
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
27
|
+
* `data-fit` lets a squares bento's columns share a measure other than its
|
|
28
|
+
* squares (each tile still reaches a quarter square, so the gap is a column
|
|
29
|
+
* less half a square); `data-layout` (say `phone`) makes `placeBento()` take each tile's
|
|
30
|
+
* `data-place-phone` where it has one, a place per layout.
|
|
31
|
+
*
|
|
32
|
+
* Tokens: `--ds-bento-unit` (the square, `--ds-unit` by default),
|
|
33
|
+
* `--ds-bento-gap` (half of it), `--ds-bento-columns`, `--ds-bento-row`,
|
|
34
|
+
* `--ds-bento-padding` (a squares tile that is not a card, one square), and
|
|
35
|
+
* on a tile `--ds-bento-span`, `--ds-bento-rows`.
|
|
29
36
|
*/
|
|
30
37
|
|
|
31
38
|
.ds-bento {
|
|
32
|
-
--ds-bento-
|
|
39
|
+
--ds-bento-unit: var(--ds-unit);
|
|
40
|
+
--ds-bento-gap: calc(var(--ds-bento-unit) / 2);
|
|
33
41
|
box-sizing: border-box;
|
|
34
42
|
display: grid;
|
|
35
43
|
grid-template-columns: repeat(var(--ds-bento-columns, 3), minmax(0, 1fr));
|
|
36
|
-
grid-auto-rows: minmax(var(--ds-bento-row, calc(10 * var(--ds-unit))), auto);
|
|
44
|
+
grid-auto-rows: minmax(var(--ds-bento-row, calc(10 * var(--ds-bento-unit))), auto);
|
|
37
45
|
grid-auto-flow: row dense;
|
|
38
46
|
gap: var(--ds-bento-gap);
|
|
39
47
|
margin: 0;
|
|
@@ -69,15 +77,20 @@
|
|
|
69
77
|
|
|
70
78
|
/* Squares: every tile on whole squares, a square apart, its surface a quarter square nearer each neighbour. */
|
|
71
79
|
.ds-bento[data-squares] {
|
|
72
|
-
--ds-bento-reach: calc(var(--ds-unit) / 4);
|
|
80
|
+
--ds-bento-reach: calc(var(--ds-bento-unit) / 4);
|
|
73
81
|
/* Exactly its squares wide: each column one square. */
|
|
74
|
-
width: calc(var(--ds-bento-columns, 48) * var(--ds-unit));
|
|
82
|
+
width: calc(var(--ds-bento-columns, 48) * var(--ds-bento-unit));
|
|
75
83
|
max-width: none;
|
|
76
|
-
grid-template-columns: repeat(var(--ds-bento-columns, 48), var(--ds-unit));
|
|
77
|
-
grid-auto-rows: var(--ds-unit);
|
|
84
|
+
grid-template-columns: repeat(var(--ds-bento-columns, 48), var(--ds-bento-unit));
|
|
85
|
+
grid-auto-rows: var(--ds-bento-unit);
|
|
78
86
|
grid-auto-flow: row;
|
|
79
87
|
gap: 0;
|
|
80
88
|
}
|
|
89
|
+
/* Fit: the columns share a measure narrower (or wider) than the squares, the rows still one square. */
|
|
90
|
+
.ds-bento[data-squares][data-fit] {
|
|
91
|
+
width: auto;
|
|
92
|
+
grid-template-columns: repeat(var(--ds-bento-columns, 48), minmax(0, 1fr));
|
|
93
|
+
}
|
|
81
94
|
.ds-bento[data-squares] > * {
|
|
82
95
|
--ds-bento-inset-top: calc(var(--ds-bento-reach) * var(--ds-bento-top, 0));
|
|
83
96
|
--ds-bento-inset-end: calc(var(--ds-bento-reach) * var(--ds-bento-end, 0));
|
|
@@ -90,8 +103,8 @@
|
|
|
90
103
|
/* A card grows its own padding by the reach (card.css); any other tile is padded here, a square inside. */
|
|
91
104
|
.ds-bento[data-squares] > :not(.ds-card, ds-card) {
|
|
92
105
|
padding:
|
|
93
|
-
calc(var(--ds-unit) + var(--ds-bento-inset-top))
|
|
94
|
-
calc(var(--ds-unit) + var(--ds-bento-inset-end))
|
|
95
|
-
calc(var(--ds-unit) + var(--ds-bento-inset-bottom))
|
|
96
|
-
calc(var(--ds-unit) + var(--ds-bento-inset-start));
|
|
106
|
+
calc(var(--ds-bento-padding, var(--ds-bento-unit)) + var(--ds-bento-inset-top))
|
|
107
|
+
calc(var(--ds-bento-padding, var(--ds-bento-unit)) + var(--ds-bento-inset-end))
|
|
108
|
+
calc(var(--ds-bento-padding, var(--ds-bento-unit)) + var(--ds-bento-inset-bottom))
|
|
109
|
+
calc(var(--ds-bento-padding, var(--ds-bento-unit)) + var(--ds-bento-inset-start));
|
|
97
110
|
}
|
package/DS1/1-root/cjk.css
CHANGED
package/DS1/1-root/controls.css
CHANGED
|
@@ -226,8 +226,10 @@ textarea.ds-input {
|
|
|
226
226
|
|
|
227
227
|
/*
|
|
228
228
|
* `.ds-checkbox` and `.ds-radio` are the `ds-checkbox` and `ds-radio` elements'
|
|
229
|
-
* faces on a native `<input>`: one square on the field's
|
|
230
|
-
* line
|
|
229
|
+
* faces on a native `<input>`: a box one control (16px) square on the field's
|
|
230
|
+
* ground with a 1px line, standing as an icon does, on the lower sixteen at the
|
|
231
|
+
* start of its square, so it takes one square and the words begin on the next
|
|
232
|
+
* (Joachim, 19 October 2026). The checkbox has the corners of a control and fills with the accent,
|
|
231
233
|
* the check (`x-icon/check.svg`) in its ink; the radio is a circle with a dot
|
|
232
234
|
* half a square across in the text colour.
|
|
233
235
|
*/
|
|
@@ -238,10 +240,12 @@ textarea.ds-input {
|
|
|
238
240
|
display: inline-grid;
|
|
239
241
|
flex: 0 0 auto;
|
|
240
242
|
place-items: center;
|
|
241
|
-
vertical-align:
|
|
242
|
-
width: var(--ds-
|
|
243
|
-
height: var(--ds-
|
|
243
|
+
vertical-align: top;
|
|
244
|
+
width: var(--ds-control-height);
|
|
245
|
+
height: var(--ds-control-height);
|
|
244
246
|
margin: 0;
|
|
247
|
+
margin-block-start: calc(var(--ds-row-height) - var(--ds-control-height));
|
|
248
|
+
margin-inline-end: calc(var(--ds-row-height) - var(--ds-control-height));
|
|
245
249
|
padding: 0;
|
|
246
250
|
border: 1px solid var(--ds-border-color);
|
|
247
251
|
background: var(--ds-input-background);
|
|
@@ -267,13 +271,13 @@ textarea.ds-input {
|
|
|
267
271
|
}
|
|
268
272
|
|
|
269
273
|
.ds-checkbox::before {
|
|
270
|
-
width:
|
|
271
|
-
height:
|
|
274
|
+
width: var(--ds-control-height);
|
|
275
|
+
height: var(--ds-control-height);
|
|
272
276
|
mask: var(--ds-checkbox-mark) center / contain no-repeat;
|
|
273
277
|
}
|
|
274
278
|
|
|
275
279
|
.ds-checkbox:indeterminate::before {
|
|
276
|
-
width: calc(var(--ds-
|
|
280
|
+
width: calc(var(--ds-control-height) / 2);
|
|
277
281
|
height: calc(2px * var(--ds-sf));
|
|
278
282
|
mask: none;
|
|
279
283
|
}
|
|
@@ -284,8 +288,8 @@ textarea.ds-input {
|
|
|
284
288
|
}
|
|
285
289
|
|
|
286
290
|
.ds-radio::before {
|
|
287
|
-
width: calc(var(--ds-
|
|
288
|
-
height: calc(var(--ds-
|
|
291
|
+
width: calc(var(--ds-control-height) / 2);
|
|
292
|
+
height: calc(var(--ds-control-height) / 2);
|
|
289
293
|
border-radius: 50%;
|
|
290
294
|
}
|
|
291
295
|
|
|
@@ -297,8 +301,9 @@ textarea.ds-input {
|
|
|
297
301
|
/*
|
|
298
302
|
* `.ds-switch` is the `ds-switch` element's look on a native
|
|
299
303
|
* `<button role="switch" aria-checked>`: two control heights wide, its thumb
|
|
300
|
-
* one control high less a 2px inset, travelling one control height.
|
|
301
|
-
*
|
|
304
|
+
* one control high less a 2px inset, travelling one control height. A pill with a white
|
|
305
|
+
* round thumb, grey when off and the darker green when on, whatever the page's accent: the
|
|
306
|
+
* one switch (Joachim, 19 October 2026).
|
|
302
307
|
*/
|
|
303
308
|
|
|
304
309
|
.ds-switch {
|
|
@@ -314,9 +319,9 @@ textarea.ds-input {
|
|
|
314
319
|
margin: 0;
|
|
315
320
|
padding: calc(2px * var(--ds-sf));
|
|
316
321
|
border: 0;
|
|
317
|
-
border-radius: var(--ds-
|
|
322
|
+
border-radius: var(--ds-choice-unit);
|
|
318
323
|
background: var(--ds-fill);
|
|
319
|
-
color: var(--ds-
|
|
324
|
+
color: var(--ds-white);
|
|
320
325
|
cursor: pointer;
|
|
321
326
|
touch-action: manipulation;
|
|
322
327
|
}
|
|
@@ -326,35 +331,20 @@ textarea.ds-input {
|
|
|
326
331
|
display: block;
|
|
327
332
|
width: calc(var(--ds-choice-unit) - 4px * var(--ds-sf));
|
|
328
333
|
height: 100%;
|
|
329
|
-
border-radius:
|
|
330
|
-
background:
|
|
334
|
+
border-radius: 50%;
|
|
335
|
+
background: var(--ds-white);
|
|
331
336
|
transform: translateX(0);
|
|
332
337
|
transition: transform 140ms ease;
|
|
333
338
|
}
|
|
334
339
|
|
|
335
340
|
.ds-switch[aria-checked="true"] {
|
|
336
|
-
background: var(--ds-
|
|
337
|
-
color: var(--ds-button-text-color-on-accent);
|
|
341
|
+
background: var(--ds-switch-track-on);
|
|
338
342
|
}
|
|
339
343
|
|
|
340
344
|
.ds-switch[aria-checked="true"]::after {
|
|
341
345
|
transform: translateX(var(--ds-choice-unit));
|
|
342
346
|
}
|
|
343
347
|
|
|
344
|
-
.ds-switch--rounded {
|
|
345
|
-
border-radius: var(--ds-choice-unit);
|
|
346
|
-
color: var(--ds-white);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
.ds-switch--rounded::after {
|
|
350
|
-
border-radius: 50%;
|
|
351
|
-
background: var(--ds-white);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
.ds-switch--rounded[aria-checked="true"] {
|
|
355
|
-
background: var(--ds-switch-track-on);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
348
|
@media (prefers-reduced-motion: reduce) {
|
|
359
349
|
.ds-switch::after {
|
|
360
350
|
transition: none;
|
package/DS1/1-root/fonts.css
CHANGED
package/DS1/1-root/one.css
CHANGED
package/DS1/2-core/ds-switch.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { LitElement, html, unsafeCSS } from "lit";
|
|
|
2
2
|
import { define } from "../0-face/define";
|
|
3
3
|
import styles from "./styles/ds-switch.css?inline";
|
|
4
4
|
|
|
5
|
-
/** The
|
|
6
|
-
|
|
5
|
+
/** The switch: a pill with a white round thumb, grey when off and the darker green when on,
|
|
6
|
+
whatever the page's accent. The slotted button retains native activation and refs. */
|
|
7
7
|
export class Switch extends LitElement {
|
|
8
8
|
static styles = unsafeCSS(styles);
|
|
9
9
|
focus(options?: FocusOptions) { this.querySelector<HTMLButtonElement>(":scope > button")?.focus(options); }
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
@import "../../1-root/component-metrics.css";
|
|
2
2
|
|
|
3
|
+
/* One square, the box one control (16px) square on its lower sixteen at its start, as an icon
|
|
4
|
+
stands, so the words begin on the next square (Joachim, 19 October 2026). */
|
|
3
5
|
:host {
|
|
4
6
|
--ds-choice-unit: calc(var(--ds-1) * var(--ds-sf));
|
|
5
|
-
display: inline-
|
|
7
|
+
display: inline-grid;
|
|
8
|
+
align-items: end;
|
|
9
|
+
justify-items: start;
|
|
6
10
|
flex: 0 0 auto;
|
|
7
|
-
vertical-align:
|
|
11
|
+
vertical-align: top;
|
|
8
12
|
width: var(--ds-choice-unit);
|
|
9
13
|
height: var(--ds-choice-unit);
|
|
10
14
|
}
|
|
@@ -14,9 +18,9 @@
|
|
|
14
18
|
box-sizing: border-box !important;
|
|
15
19
|
display: grid !important;
|
|
16
20
|
place-items: center !important;
|
|
17
|
-
width:
|
|
21
|
+
width: var(--ds-control-height) !important;
|
|
18
22
|
min-width: 0 !important;
|
|
19
|
-
height:
|
|
23
|
+
height: var(--ds-control-height) !important;
|
|
20
24
|
min-height: 0 !important;
|
|
21
25
|
margin: 0 !important;
|
|
22
26
|
padding: 0 !important;
|
|
@@ -31,15 +35,15 @@
|
|
|
31
35
|
::slotted(input:checked), ::slotted(input:indeterminate) { background: var(--ds-accent-color) !important; }
|
|
32
36
|
::slotted(input)::before {
|
|
33
37
|
content: "";
|
|
34
|
-
width:
|
|
35
|
-
height:
|
|
38
|
+
width: var(--ds-control-height);
|
|
39
|
+
height: var(--ds-control-height);
|
|
36
40
|
background: currentColor;
|
|
37
41
|
mask: var(--ds-checkbox-mark) center / contain no-repeat;
|
|
38
42
|
opacity: 0;
|
|
39
43
|
}
|
|
40
44
|
::slotted(input:checked)::before { opacity: 1; }
|
|
41
45
|
::slotted(input:indeterminate)::before {
|
|
42
|
-
width: calc(var(--ds-
|
|
46
|
+
width: calc(var(--ds-control-height) / 2);
|
|
43
47
|
height: calc(2px * var(--ds-sf));
|
|
44
48
|
mask: none;
|
|
45
49
|
opacity: 1;
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
height: var(--ds-unit);
|
|
10
10
|
}
|
|
11
11
|
:host([hidden]) { display: none !important; }
|
|
12
|
+
/* A plain radio as the checkbox: a 16px circle on its square's lower sixteen, at its start. */
|
|
13
|
+
:host(:not([choice])) { display: inline-grid; align-items: end; justify-items: start; vertical-align: top; }
|
|
14
|
+
:host(:not([choice])) ::slotted(input[type="radio"]) { width: var(--ds-control-height) !important; height: var(--ds-control-height) !important; }
|
|
12
15
|
::slotted(input[type="radio"]) {
|
|
13
16
|
appearance: none !important;
|
|
14
17
|
box-sizing: border-box !important;
|
|
@@ -30,8 +33,8 @@
|
|
|
30
33
|
::slotted(input[hidden]) { display: none !important; }
|
|
31
34
|
::slotted(input)::before {
|
|
32
35
|
content: "";
|
|
33
|
-
width: calc(var(--ds-
|
|
34
|
-
height: calc(var(--ds-
|
|
36
|
+
width: calc(var(--ds-control-height) / 2);
|
|
37
|
+
height: calc(var(--ds-control-height) / 2);
|
|
35
38
|
border-radius: 50%;
|
|
36
39
|
background: currentColor;
|
|
37
40
|
opacity: 0;
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
padding: calc(2px * var(--ds-sf)) !important;
|
|
22
22
|
margin: 0 !important;
|
|
23
23
|
border: 0 !important;
|
|
24
|
-
border-radius: var(--ds-
|
|
25
|
-
corner-shape:
|
|
24
|
+
border-radius: var(--ds-choice-unit) !important;
|
|
25
|
+
corner-shape: round !important;
|
|
26
26
|
background: var(--ds-fill) !important;
|
|
27
|
-
color: var(--ds-
|
|
27
|
+
color: var(--ds-white) !important;
|
|
28
28
|
box-shadow: none !important;
|
|
29
29
|
cursor: pointer !important;
|
|
30
30
|
touch-action: manipulation;
|
|
@@ -34,32 +34,16 @@
|
|
|
34
34
|
display: block;
|
|
35
35
|
width: calc(var(--ds-choice-unit) - 4px * var(--ds-sf));
|
|
36
36
|
height: 100%;
|
|
37
|
-
border-radius:
|
|
38
|
-
corner-shape:
|
|
39
|
-
background:
|
|
37
|
+
border-radius: 50%;
|
|
38
|
+
corner-shape: round;
|
|
39
|
+
background: var(--ds-white);
|
|
40
40
|
transform: translateX(0);
|
|
41
41
|
transition: transform 140ms ease;
|
|
42
42
|
}
|
|
43
43
|
::slotted(button[aria-checked="true"]) {
|
|
44
|
-
background: var(--ds-
|
|
45
|
-
color: var(--ds-button-text-color-on-accent) !important;
|
|
44
|
+
background: var(--ds-switch-track-on) !important;
|
|
46
45
|
}
|
|
47
46
|
::slotted(button[aria-checked="true"])::after { transform: translateX(var(--ds-choice-unit)); }
|
|
48
47
|
::slotted(button:focus-visible) { outline: 2px solid var(--ds-focus-ring) !important; outline-offset: 1px !important; }
|
|
49
48
|
::slotted(button:disabled) { cursor: default !important; opacity: .5 !important; }
|
|
50
49
|
@media (prefers-reduced-motion: reduce) { ::slotted(button)::after { transition: none; } }
|
|
51
|
-
|
|
52
|
-
/* Rounded switches keep their shape and white thumb independently of page accents. */
|
|
53
|
-
:host([rounded]) ::slotted(button) {
|
|
54
|
-
border-radius: var(--ds-choice-unit) !important;
|
|
55
|
-
corner-shape: round !important;
|
|
56
|
-
color: var(--ds-white) !important;
|
|
57
|
-
}
|
|
58
|
-
:host([rounded]) ::slotted(button)::after {
|
|
59
|
-
border-radius: 50%;
|
|
60
|
-
corner-shape: round;
|
|
61
|
-
background: var(--ds-white);
|
|
62
|
-
}
|
|
63
|
-
:host([rounded]) ::slotted(button[aria-checked="true"]) {
|
|
64
|
-
background: var(--ds-switch-track-on) !important;
|
|
65
|
-
}
|
package/DS1/3-unit/ds-bento.ts
CHANGED
|
@@ -16,12 +16,18 @@ export class Bento extends LitElement {
|
|
|
16
16
|
static properties = {
|
|
17
17
|
columns: { type: Number, reflect: true },
|
|
18
18
|
squares: { type: Boolean, reflect: true },
|
|
19
|
+
fit: { type: Boolean, reflect: true },
|
|
20
|
+
layout: { type: String, reflect: true },
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
/** The number of columns (flow, three by default), or the squares across (`squares`, 48 by default). */
|
|
22
24
|
declare columns: number | undefined;
|
|
23
25
|
/** Places every tile on whole squares by its `data-place`. */
|
|
24
26
|
declare squares: boolean;
|
|
27
|
+
/** With `squares`, lets the columns share a measure other than the squares. */
|
|
28
|
+
declare fit: boolean;
|
|
29
|
+
/** Takes each tile's `data-place-<layout>` where it has one (say `phone`). */
|
|
30
|
+
declare layout: string | undefined;
|
|
25
31
|
|
|
26
32
|
static styles = [unsafeCSS(metrics), unsafeCSS(styles)];
|
|
27
33
|
|
|
@@ -30,12 +36,13 @@ export class Bento extends LitElement {
|
|
|
30
36
|
constructor() {
|
|
31
37
|
super();
|
|
32
38
|
this.squares = false;
|
|
39
|
+
this.fit = false;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
connectedCallback() {
|
|
36
43
|
super.connectedCallback();
|
|
37
44
|
this.observer = new MutationObserver(() => this.place());
|
|
38
|
-
this.observer.observe(this, { childList: true, subtree: true, attributes: true, attributeFilter: ["data-place", "hidden"] });
|
|
45
|
+
this.observer.observe(this, { childList: true, subtree: true, attributes: true, attributeFilter: ["data-place", "hidden"], attributeOldValue: false });
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
disconnectedCallback() {
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
dateNumber,
|
|
22
22
|
dayOf,
|
|
23
23
|
eventsByDay,
|
|
24
|
+
heatLevels,
|
|
24
25
|
isDay,
|
|
25
26
|
isoWeek,
|
|
26
27
|
monthTitle,
|
|
@@ -30,6 +31,7 @@ import {
|
|
|
30
31
|
weekdayNames,
|
|
31
32
|
type CalendarEvent,
|
|
32
33
|
type Day,
|
|
34
|
+
type HeatLevel,
|
|
33
35
|
} from "../0-face/calendar";
|
|
34
36
|
import { CalendarBase, readMonth, readRange, type CalendarChangeDetail } from "./calendar-base";
|
|
35
37
|
import "../2-core/ds-icon";
|
|
@@ -105,6 +107,12 @@ const openPickers = new Set<Calendar>();
|
|
|
105
107
|
* @prop {boolean} fixed - Always draw six weeks, so the month keeps its height.
|
|
106
108
|
* @prop {boolean} outsideDays - Draw the days of the months before and after
|
|
107
109
|
* in the title grey (`outside-days`); pressing one moves there.
|
|
110
|
+
* @prop {boolean} heat - Light each day by how much happens on it, as a
|
|
111
|
+
* contribution graph does: the events' `count`s added up (one each by
|
|
112
|
+
* default), in four steps of the busiest day shown.
|
|
113
|
+
* @prop {number} heatMax - The amount that lights a day fully (`heat-max`),
|
|
114
|
+
* for a scale that stays put from month to month; the busiest day shown
|
|
115
|
+
* by default.
|
|
108
116
|
* @prop {boolean} noHeader - Leave out the title and arrows (`no-header`).
|
|
109
117
|
* @prop {boolean} noToday - Leave out the Today button (`no-today`).
|
|
110
118
|
* @prop {string} min - The first day a person may choose.
|
|
@@ -153,6 +161,8 @@ export class Calendar extends CalendarBase {
|
|
|
153
161
|
outsideDays: { type: Boolean, attribute: "outside-days", reflect: true },
|
|
154
162
|
noHeader: { type: Boolean, attribute: "no-header", reflect: true },
|
|
155
163
|
noToday: { type: Boolean, attribute: "no-today", reflect: true },
|
|
164
|
+
heat: { type: Boolean, reflect: true },
|
|
165
|
+
heatMax: { type: Number, attribute: "heat-max" },
|
|
156
166
|
picker: { type: Boolean, reflect: true },
|
|
157
167
|
name: { type: String, reflect: true },
|
|
158
168
|
placeholder: { type: String },
|
|
@@ -175,6 +185,8 @@ export class Calendar extends CalendarBase {
|
|
|
175
185
|
declare outsideDays: boolean;
|
|
176
186
|
declare noHeader: boolean;
|
|
177
187
|
declare noToday: boolean;
|
|
188
|
+
declare heat: boolean;
|
|
189
|
+
declare heatMax: number | undefined;
|
|
178
190
|
declare picker: boolean;
|
|
179
191
|
declare name: string | undefined;
|
|
180
192
|
declare placeholder: string | undefined;
|
|
@@ -189,6 +201,7 @@ export class Calendar extends CalendarBase {
|
|
|
189
201
|
protected readonly weeksRun = "rows" as const;
|
|
190
202
|
|
|
191
203
|
private byDay = new Map<Day, CalendarEvent[]>();
|
|
204
|
+
private heatByDay = new Map<Day, HeatLevel>();
|
|
192
205
|
private readonly internals = typeof this.attachInternals === "function" ? this.attachInternals() : null;
|
|
193
206
|
/** The value the field started with, for a form's reset. */
|
|
194
207
|
private initial = "";
|
|
@@ -204,6 +217,7 @@ export class Calendar extends CalendarBase {
|
|
|
204
217
|
this.outsideDays = false;
|
|
205
218
|
this.noHeader = false;
|
|
206
219
|
this.noToday = false;
|
|
220
|
+
this.heat = false;
|
|
207
221
|
this.picker = false;
|
|
208
222
|
this.time = false;
|
|
209
223
|
this.required = false;
|
|
@@ -400,7 +414,8 @@ export class Calendar extends CalendarBase {
|
|
|
400
414
|
const unfolded = this.unfolded === day;
|
|
401
415
|
const shown = unfolded || events.length <= lines ? events : events.slice(0, Math.max(0, lines - 1));
|
|
402
416
|
const hidden = events.length - shown.length;
|
|
403
|
-
const
|
|
417
|
+
const heat = this.heatByDay.get(day);
|
|
418
|
+
const parts = ["day", isToday ? "today" : "", selected ? "selected" : "", heat ? "heat" : ""].filter(Boolean).join(" ");
|
|
404
419
|
return html`<div
|
|
405
420
|
role="gridcell"
|
|
406
421
|
class="day"
|
|
@@ -418,6 +433,7 @@ export class Calendar extends CalendarBase {
|
|
|
418
433
|
?data-last=${last}
|
|
419
434
|
?data-outside=${outside}
|
|
420
435
|
?data-busy=${events.length > 0}
|
|
436
|
+
data-heat=${heat ?? nothing}
|
|
421
437
|
?data-open=${unfolded}
|
|
422
438
|
>
|
|
423
439
|
<span class="date-line" aria-hidden="true">
|
|
@@ -444,6 +460,8 @@ export class Calendar extends CalendarBase {
|
|
|
444
460
|
const first = this.firstDay;
|
|
445
461
|
const weeks = monthWeeks(month, first, this.fixed);
|
|
446
462
|
this.byDay = eventsByDay(this.events ?? [], weeks[0][0], weeks.at(-1)![6]);
|
|
463
|
+
// Heat is measured over the days of the month shown, so the neighbours' days never set its scale.
|
|
464
|
+
this.heatByDay = this.heat ? heatLevels(new Map([...this.byDay].filter(([day]) => sameMonth(day, month))), this.heatMax) : new Map();
|
|
447
465
|
// A compact month names its days in a letter (one square) or two (wider); a full month in short.
|
|
448
466
|
const short = this.compact
|
|
449
467
|
? this.squaresPerDay < 2
|
|
@@ -735,7 +753,7 @@ export class Calendar extends CalendarBase {
|
|
|
735
753
|
const name = this.label ? this.pickerWords(this.label, this.label) : this.pickerWords(undefined, "Date");
|
|
736
754
|
const days = start ? (end ? `${start.day}/${end.day}` : start.day) : "";
|
|
737
755
|
return html`<button
|
|
738
|
-
class="ds-
|
|
756
|
+
class="ds-input trigger"
|
|
739
757
|
part="trigger"
|
|
740
758
|
type="button"
|
|
741
759
|
aria-haspopup="dialog"
|
|
@@ -746,9 +764,8 @@ export class Calendar extends CalendarBase {
|
|
|
746
764
|
@click=${this.toggle}
|
|
747
765
|
@keydown=${this.onTriggerKey}
|
|
748
766
|
>
|
|
749
|
-
<ds-icon class="icon" type="calendar" aria-hidden="true"></ds-icon>
|
|
750
767
|
<span class="text">${text}</span>
|
|
751
|
-
<ds-icon class="arrow" type="down" aria-hidden="true"></ds-icon>
|
|
768
|
+
<ds-icon class="arrow" type="chevron-down" size="large" aria-hidden="true"></ds-icon>
|
|
752
769
|
</button>
|
|
753
770
|
<div
|
|
754
771
|
class="popover"
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
/* The element's bento: the same as 1-root/bento.css's .ds-bento, on the host and its slotted tiles. */
|
|
3
3
|
|
|
4
4
|
:host {
|
|
5
|
-
--ds-bento-
|
|
5
|
+
--ds-bento-unit: var(--ds-unit);
|
|
6
|
+
--ds-bento-gap: calc(var(--ds-bento-unit) / 2);
|
|
6
7
|
box-sizing: border-box;
|
|
7
8
|
display: grid;
|
|
8
9
|
grid-template-columns: repeat(var(--ds-bento-columns, 3), minmax(0, 1fr));
|
|
9
|
-
grid-auto-rows: minmax(var(--ds-bento-row, calc(10 * var(--ds-unit))), auto);
|
|
10
|
+
grid-auto-rows: minmax(var(--ds-bento-row, calc(10 * var(--ds-bento-unit))), auto);
|
|
10
11
|
grid-auto-flow: row dense;
|
|
11
12
|
gap: var(--ds-bento-gap);
|
|
12
13
|
}
|
|
@@ -38,15 +39,20 @@
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
:host([squares]) {
|
|
41
|
-
--ds-bento-reach: calc(var(--ds-unit) / 4);
|
|
42
|
+
--ds-bento-reach: calc(var(--ds-bento-unit) / 4);
|
|
42
43
|
/* Exactly its squares wide: each column one square. */
|
|
43
|
-
width: calc(var(--ds-bento-columns, 48) * var(--ds-unit));
|
|
44
|
+
width: calc(var(--ds-bento-columns, 48) * var(--ds-bento-unit));
|
|
44
45
|
max-width: none;
|
|
45
|
-
grid-template-columns: repeat(var(--ds-bento-columns, 48), var(--ds-unit));
|
|
46
|
-
grid-auto-rows: var(--ds-unit);
|
|
46
|
+
grid-template-columns: repeat(var(--ds-bento-columns, 48), var(--ds-bento-unit));
|
|
47
|
+
grid-auto-rows: var(--ds-bento-unit);
|
|
47
48
|
grid-auto-flow: row;
|
|
48
49
|
gap: 0;
|
|
49
50
|
}
|
|
51
|
+
/* Fit: the columns share a measure narrower (or wider) than the squares, the rows still one square. */
|
|
52
|
+
:host([squares][fit]) {
|
|
53
|
+
width: auto;
|
|
54
|
+
grid-template-columns: repeat(var(--ds-bento-columns, 48), minmax(0, 1fr));
|
|
55
|
+
}
|
|
50
56
|
:host([squares]) ::slotted(*) {
|
|
51
57
|
--ds-bento-inset-top: calc(var(--ds-bento-reach) * var(--ds-bento-top, 0));
|
|
52
58
|
--ds-bento-inset-end: calc(var(--ds-bento-reach) * var(--ds-bento-end, 0));
|
|
@@ -59,8 +65,8 @@
|
|
|
59
65
|
/* A card grows its own padding by the reach (card.css); any other tile is padded here, a square inside. */
|
|
60
66
|
:host([squares]) ::slotted(:not(.ds-card, ds-card)) {
|
|
61
67
|
padding:
|
|
62
|
-
calc(var(--ds-unit) + var(--ds-bento-inset-top))
|
|
63
|
-
calc(var(--ds-unit) + var(--ds-bento-inset-end))
|
|
64
|
-
calc(var(--ds-unit) + var(--ds-bento-inset-bottom))
|
|
65
|
-
calc(var(--ds-unit) + var(--ds-bento-inset-start));
|
|
68
|
+
calc(var(--ds-bento-padding, var(--ds-bento-unit)) + var(--ds-bento-inset-top))
|
|
69
|
+
calc(var(--ds-bento-padding, var(--ds-bento-unit)) + var(--ds-bento-inset-end))
|
|
70
|
+
calc(var(--ds-bento-padding, var(--ds-bento-unit)) + var(--ds-bento-inset-bottom))
|
|
71
|
+
calc(var(--ds-bento-padding, var(--ds-bento-unit)) + var(--ds-bento-inset-start));
|
|
66
72
|
}
|