@0001-labs/design-system-1 0.3.0-alpha.20 → 0.3.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/DS1/0-face/bento.ts +84 -0
  2. package/DS1/0-face/calendar.ts +2 -2
  3. package/DS1/1-root/bento.css +97 -0
  4. package/DS1/1-root/card.css +6 -1
  5. package/DS1/1-root/cjk.css +1 -1
  6. package/DS1/1-root/controls.css +1 -0
  7. package/DS1/1-root/fonts.css +1 -1
  8. package/DS1/1-root/one.css +1 -1
  9. package/DS1/1-root/one.semantic.css +1 -1
  10. package/DS1/3-unit/calendar-base.ts +31 -22
  11. package/DS1/3-unit/ds-bento.ts +67 -0
  12. package/DS1/3-unit/ds-calendar.ts +477 -22
  13. package/DS1/3-unit/styles/ds-bento.css +66 -0
  14. package/DS1/3-unit/styles/ds-calendar.css +119 -1
  15. package/DS1/index.ts +1 -1
  16. package/dist/0-face/bento.d.ts +41 -0
  17. package/dist/0-face/bento.d.ts.map +1 -0
  18. package/dist/0-face/bento.js +77 -0
  19. package/dist/0-face/calendar.js +2 -2
  20. package/dist/3-unit/calendar-base.d.ts +17 -12
  21. package/dist/3-unit/calendar-base.d.ts.map +1 -1
  22. package/dist/3-unit/calendar-base.js +21 -16
  23. package/dist/3-unit/ds-bento.d.ts +31 -0
  24. package/dist/3-unit/ds-bento.d.ts.map +1 -0
  25. package/dist/3-unit/ds-bento.js +47 -0
  26. package/dist/3-unit/ds-calendar-large.d.ts +3 -4
  27. package/dist/3-unit/ds-calendar-large.d.ts.map +1 -1
  28. package/dist/3-unit/ds-calendar.d.ts +139 -12
  29. package/dist/3-unit/ds-calendar.d.ts.map +1 -1
  30. package/dist/3-unit/ds-calendar.js +464 -23
  31. package/dist/design-system-1.bundle.js +1124 -1094
  32. package/dist/design-system-1.bundle.js.map +1 -1
  33. package/dist/design-system-1.bundle.min.js +2483 -2456
  34. package/dist/design-system-1.bundle.min.js.map +1 -1
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +1 -1
  38. package/package.json +1 -1
  39. package/DS1/3-unit/ds-date-picker.ts +0 -540
  40. package/DS1/3-unit/styles/ds-date-picker.css +0 -124
  41. package/dist/3-unit/ds-date-picker.d.ts +0 -203
  42. package/dist/3-unit/ds-date-picker.d.ts.map +0 -1
  43. package/dist/3-unit/ds-date-picker.js +0 -499
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Bentos without Lit: places the tiles of a `.ds-bento[data-squares]` (drawn
3
+ * by `1-root/bento.css`) on their squares and lets each surface reach a
4
+ * quarter square towards the tiles beside it, so the tiles stand half a
5
+ * square apart while what they hold stays on the squares. A flow bento needs
6
+ * no script. No framework and no imports.
7
+ *
8
+ * ```html
9
+ * <div class="ds-bento" data-squares style="--ds-bento-columns: 48">
10
+ * <a class="ds-card" href="/archive/" data-place="0 0 15 7">…</a>
11
+ * <a class="ds-card" href="/ezo/" data-place="16 0 16 20">…</a>
12
+ * </div>
13
+ * <script type="module">
14
+ * import { placeBentos } from "/ds1/0-face/bento.js";
15
+ * placeBentos();
16
+ * </script>
17
+ * ```
18
+ *
19
+ * A tile's `data-place` is its square across, its square down, its width and
20
+ * its height, in whole squares counted from 0. Tiles are placed a square
21
+ * apart; a tile a square away from another, with the two overlapping along
22
+ * that side, reaches towards it.
23
+ */
24
+
25
+ /** A tile's place in squares: across, down, wide, high. */
26
+ export type BentoPlace = readonly [x: number, y: number, w: number, h: number];
27
+
28
+ /** Which sides of each tile face another tile a square away. */
29
+ export interface BentoReach { start: boolean; top: boolean; end: boolean; bottom: boolean }
30
+
31
+ /** Reads `data-place` ("x y w h"); `undefined` unless it is four whole numbers with a size. */
32
+ export function bentoPlace(value: string | null | undefined): BentoPlace | undefined {
33
+ const parts = (value ?? "").trim().split(/[\s,]+/).map(Number);
34
+ if (parts.length !== 4 || parts.some(part => !Number.isInteger(part) || part < 0)) return undefined;
35
+ const [x, y, w, h] = parts as [number, number, number, number];
36
+ return w > 0 && h > 0 ? [x, y, w, h] : undefined;
37
+ }
38
+
39
+ const overlaps = (a0: number, a1: number, b0: number, b1: number) => a0 < b1 && b0 < a1;
40
+
41
+ /** For each place, the sides that face another tile across a one-square gap. */
42
+ export function bentoReach(places: readonly BentoPlace[]): BentoReach[] {
43
+ return places.map(([x, y, w, h], index) => {
44
+ const others = places.filter((_, other) => other !== index);
45
+ return {
46
+ start: others.some(([ox, oy, ow, oh]) => ox + ow + 1 === x && overlaps(y, y + h, oy, oy + oh)),
47
+ end: others.some(([ox, oy, , oh]) => x + w + 1 === ox && overlaps(y, y + h, oy, oy + oh)),
48
+ top: others.some(([ox, oy, ow, oh]) => oy + oh + 1 === y && overlaps(x, x + w, ox, ox + ow)),
49
+ bottom: others.some(([ox, oy, ow]) => y + h + 1 === oy && overlaps(x, x + w, ox, ox + ow)),
50
+ };
51
+ });
52
+ }
53
+
54
+ /** Places one squares bento's tiles: their squares and their reach, as custom properties. */
55
+ export function placeBento(bento: HTMLElement): void {
56
+ const tiles = [...bento.children].filter((child): child is HTMLElement => child instanceof HTMLElement && !child.hidden);
57
+ const placed = tiles.map(tile => ({ tile, place: bentoPlace(tile.dataset.place) })).filter((entry): entry is { tile: HTMLElement; place: BentoPlace } => !!entry.place);
58
+ const reach = bentoReach(placed.map(entry => entry.place));
59
+ placed.forEach(({ tile, place: [x, y, w, h] }, index) => {
60
+ const sides = reach[index]!;
61
+ tile.style.setProperty("--ds-bento-x", String(x));
62
+ tile.style.setProperty("--ds-bento-y", String(y));
63
+ tile.style.setProperty("--ds-bento-w", String(w));
64
+ tile.style.setProperty("--ds-bento-h", String(h));
65
+ for (const side of ["start", "top", "end", "bottom"] as const) tile.style.setProperty(`--ds-bento-${side}`, sides[side] ? "1" : "0");
66
+ });
67
+ }
68
+
69
+ /** Places every squares bento on the page (or under `root`), and again when its tiles change. */
70
+ export function placeBentos(root: ParentNode = document): () => void {
71
+ const bentos = [...root.querySelectorAll<HTMLElement>(".ds-bento[data-squares]")];
72
+ const observer = new MutationObserver(records => {
73
+ for (const record of records) {
74
+ const bento = record.target instanceof HTMLElement && record.target.matches(".ds-bento[data-squares]") ? record.target : (record.target as HTMLElement).parentElement;
75
+ if (bento?.matches(".ds-bento[data-squares]")) placeBento(bento);
76
+ }
77
+ });
78
+ for (const bento of bentos) {
79
+ placeBento(bento);
80
+ observer.observe(bento, { childList: true, subtree: false, attributes: true, attributeFilter: ["data-place", "hidden"] });
81
+ for (const tile of bento.children) observer.observe(tile, { attributes: true, attributeFilter: ["data-place", "hidden"] });
82
+ }
83
+ return () => observer.disconnect();
84
+ }
@@ -1,8 +1,8 @@
1
1
  // calendar.ts
2
2
  // Days as the calendars count them: a day is written `YYYY-MM-DD` and read on
3
3
  // its own date in every zone, a month is the whole weeks that hold it, and the
4
- // week starts where the language starts it. Shared by ds-calendar,
5
- // ds-calendar-large and ds-date-picker; no Lit, so a page can use it alone.
4
+ // week starts where the language starts it. Shared by ds-calendar (its
5
+ // picker too) and ds-calendar-large; no Lit, so a page can use it alone.
6
6
 
7
7
  import { currentLanguage } from "./i18n";
8
8
  import { dateFormatter, localeTag } from "./format";
@@ -0,0 +1,97 @@
1
+ @import "./component-metrics.css";
2
+
3
+ /*
4
+ * A bento, as plain classes on native elements: tiles of different sizes
5
+ * standing half a square (10px) apart, each a card (`.ds-card`, or any box)
6
+ * that holds one thing. The `ds-bento` element draws the same.
7
+ *
8
+ * Flow (the default): columns that share the width (`--ds-bento-columns`,
9
+ * three), a tile one column wide or more (`data-span`, or
10
+ * `--ds-bento-span`) and one row high or more (`data-rows`, or
11
+ * `--ds-bento-rows`); a row is as high as its tallest tile, at least
12
+ * `--ds-bento-row` (ten squares by default). Below thirty squares of width
13
+ * the bento is one column.
14
+ *
15
+ * Squares (`data-squares`): the bento is exactly `--ds-bento-columns` squares wide
16
+ * and every tile is placed on whole squares (`data-place="x y w h"`,
17
+ * counted from 0). Tiles are placed a square apart, and a tile's surface
18
+ * reaches a quarter square towards each tile beside it, so they stand half a
19
+ * square apart while what they hold stays on the squares, and the bento's
20
+ * own edges stay on the page's. `placeBento()` (0-face/bento.ts) reads the
21
+ * places and sets the reach; the element does it itself.
22
+ *
23
+ * data-span="2" a tile two columns wide (to 6)
24
+ * data-rows="2" a tile two rows high (to 6)
25
+ * data-place="0 0 15 7" a tile's square, width and height in squares
26
+ *
27
+ * Tokens: `--ds-bento-gap` (half a square), `--ds-bento-columns`,
28
+ * `--ds-bento-row`, and on a tile `--ds-bento-span`, `--ds-bento-rows`.
29
+ */
30
+
31
+ .ds-bento {
32
+ --ds-bento-gap: calc(var(--ds-unit) / 2);
33
+ box-sizing: border-box;
34
+ display: grid;
35
+ 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);
37
+ grid-auto-flow: row dense;
38
+ gap: var(--ds-bento-gap);
39
+ margin: 0;
40
+ padding: 0;
41
+ list-style: none;
42
+ }
43
+ .ds-bento[hidden] { display: none; }
44
+
45
+ .ds-bento > * {
46
+ --ds-card-width: auto;
47
+ --ds-card-height: auto;
48
+ box-sizing: border-box;
49
+ min-width: 0;
50
+ min-height: 0;
51
+ grid-column: span var(--ds-bento-span, 1);
52
+ grid-row: span var(--ds-bento-rows, 1);
53
+ }
54
+ .ds-bento > [data-span="2"] { --ds-bento-span: 2; }
55
+ .ds-bento > [data-span="3"] { --ds-bento-span: 3; }
56
+ .ds-bento > [data-span="4"] { --ds-bento-span: 4; }
57
+ .ds-bento > [data-span="5"] { --ds-bento-span: 5; }
58
+ .ds-bento > [data-span="6"] { --ds-bento-span: 6; }
59
+ .ds-bento > [data-rows="2"] { --ds-bento-rows: 2; }
60
+ .ds-bento > [data-rows="3"] { --ds-bento-rows: 3; }
61
+ .ds-bento > [data-rows="4"] { --ds-bento-rows: 4; }
62
+ .ds-bento > [data-rows="5"] { --ds-bento-rows: 5; }
63
+ .ds-bento > [data-rows="6"] { --ds-bento-rows: 6; }
64
+
65
+ @media (max-width: 600px) {
66
+ .ds-bento:not([data-squares]) { grid-template-columns: minmax(0, 1fr); }
67
+ .ds-bento:not([data-squares]) > * { grid-column: auto; }
68
+ }
69
+
70
+ /* Squares: every tile on whole squares, a square apart, its surface a quarter square nearer each neighbour. */
71
+ .ds-bento[data-squares] {
72
+ --ds-bento-reach: calc(var(--ds-unit) / 4);
73
+ /* Exactly its squares wide: each column one square. */
74
+ width: calc(var(--ds-bento-columns, 48) * var(--ds-unit));
75
+ max-width: none;
76
+ grid-template-columns: repeat(var(--ds-bento-columns, 48), var(--ds-unit));
77
+ grid-auto-rows: var(--ds-unit);
78
+ grid-auto-flow: row;
79
+ gap: 0;
80
+ }
81
+ .ds-bento[data-squares] > * {
82
+ --ds-bento-inset-top: calc(var(--ds-bento-reach) * var(--ds-bento-top, 0));
83
+ --ds-bento-inset-end: calc(var(--ds-bento-reach) * var(--ds-bento-end, 0));
84
+ --ds-bento-inset-bottom: calc(var(--ds-bento-reach) * var(--ds-bento-bottom, 0));
85
+ --ds-bento-inset-start: calc(var(--ds-bento-reach) * var(--ds-bento-start, 0));
86
+ grid-column: calc(var(--ds-bento-x, 0) + 1) / span var(--ds-bento-w, 4);
87
+ grid-row: calc(var(--ds-bento-y, 0) + 1) / span var(--ds-bento-h, 4);
88
+ margin: calc(-1 * var(--ds-bento-inset-top)) calc(-1 * var(--ds-bento-inset-end)) calc(-1 * var(--ds-bento-inset-bottom)) calc(-1 * var(--ds-bento-inset-start));
89
+ }
90
+ /* A card grows its own padding by the reach (card.css); any other tile is padded here, a square inside. */
91
+ .ds-bento[data-squares] > :not(.ds-card, ds-card) {
92
+ 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));
97
+ }
@@ -45,7 +45,12 @@
45
45
  height: var(--ds-card-height, auto);
46
46
  min-width: 0;
47
47
  margin: 0;
48
- padding: var(--ds-card-padding) var(--ds-card-padding) 0;
48
+ /* In a squares bento the surface reaches a quarter square towards its neighbours (`--ds-bento-inset-*`, 0 elsewhere), and the padding grows by as much so what it holds stays on the squares. */
49
+ padding:
50
+ calc(var(--ds-card-padding) + var(--ds-bento-inset-top, 0px))
51
+ calc(var(--ds-card-padding) + var(--ds-bento-inset-end, 0px))
52
+ var(--ds-bento-inset-bottom, 0px)
53
+ calc(var(--ds-card-padding) + var(--ds-bento-inset-start, 0px));
49
54
  overflow: hidden;
50
55
  border-radius: var(--ds-radius-card);
51
56
  background: var(--ds-card-fill);
@@ -1,4 +1,4 @@
1
- /* version 0.3.0-alpha.20 */
1
+ /* version 0.3.0-alpha.21 */
2
2
 
3
3
  /* Noto Sans for Japanese and Chinese, for a product that shows them. Opt in:
4
4
  link it beside one.css or fonts.css, or ask Sync 1 for "cjk": true. Without
@@ -7,6 +7,7 @@
7
7
  @import "./header.css";
8
8
  @import "./footer.css";
9
9
  @import "./document.css";
10
+ @import "./bento.css";
10
11
  @import "./carousel.css";
11
12
  @import "./card.css";
12
13
  @import "./figures.css";
@@ -1,4 +1,4 @@
1
- /* version 0.3.0-alpha.20 */
1
+ /* version 0.3.0-alpha.21 */
2
2
 
3
3
  /* Design system 1's faces and the typeface tokens that name them, on their
4
4
  own: a page that does not load one.css links this sheet (one.css imports
@@ -1,4 +1,4 @@
1
- /* version 0.3.0-alpha.20 */
1
+ /* version 0.3.0-alpha.21 */
2
2
 
3
3
  @import "./grid.css";
4
4
  @import "./component-tokens.css";
@@ -1,4 +1,4 @@
1
- /* DS1 — version 0.3.0-alpha.20 */
1
+ /* DS1 — version 0.3.0-alpha.21 */
2
2
  /* A classless stylesheet for native HTML. No JavaScript is required. */
3
3
 
4
4
  /* It draws on Design system 1's measures: the 20px square, 14px type on
@@ -37,9 +37,10 @@ export interface CalendarEventDetail {
37
37
  }
38
38
 
39
39
  /**
40
- * Detail of `change`: the value a person chose and the one before it. In a
41
- * range, `value` is `start/end` once both are chosen and the start alone
42
- * while the end is still to come.
40
+ * Detail of `change`: the value a person chose and the one before it. The
41
+ * first day pressed is `value` alone, with `end` empty while the end is still
42
+ * to come; the second ends it, and `value` is `start/end` (one day when the
43
+ * same day is pressed twice).
43
44
  */
44
45
  export interface CalendarChangeDetail {
45
46
  value: string;
@@ -77,15 +78,15 @@ export abstract class CalendarBase extends LitElement {
77
78
  today: { type: String },
78
79
  weekStart: { type: String, attribute: "week-start" },
79
80
  selectable: { type: Boolean, reflect: true },
80
- range: { type: Boolean, reflect: true },
81
81
  label: { type: String },
82
82
  literal: { type: Boolean, reflect: true },
83
83
  events: { attribute: false },
84
84
  focusDay: { state: true },
85
85
  hoverDay: { state: true },
86
+ endPending: { state: true },
86
87
  };
87
88
 
88
- /** The selected day, `YYYY-MM-DD`, or with `range` the days `YYYY-MM-DD/YYYY-MM-DD`; empty for none. */
89
+ /** The selected day, `YYYY-MM-DD`, or a range of days, `YYYY-MM-DD/YYYY-MM-DD`; empty for none. */
89
90
  declare value: string;
90
91
  /** The first day a person may choose. */
91
92
  declare min: string | undefined;
@@ -95,10 +96,8 @@ export abstract class CalendarBase extends LitElement {
95
96
  declare today: string | undefined;
96
97
  /** The first day of the week: `0`–`6` from Sunday, or a day's English name; the language's own when left out. */
97
98
  declare weekStart: string | undefined;
98
- /** Pressing a day selects it and fires `change`. */
99
+ /** Pressing a day selects it and fires `change`: one day, or a first and a last. */
99
100
  declare selectable: boolean;
100
- /** A selectable calendar chooses a first and a last day. */
101
- declare range: boolean;
102
101
  /** The grid's name: a translation key, or literal words. The month's name when left out. */
103
102
  declare label: string | undefined;
104
103
  /** Show `label` as written, never looked up. */
@@ -109,6 +108,11 @@ export abstract class CalendarBase extends LitElement {
109
108
  declare focusDay: Day | undefined;
110
109
  /** The day under the pointer, which previews a range's end. */
111
110
  declare hoverDay: Day | undefined;
111
+ /** A first day has been pressed and the next press ends the range. */
112
+ protected declare endPending: boolean;
113
+
114
+ /** The value the last press wrote, so a value set from outside settles the range. */
115
+ private pressedValue: string | undefined;
112
116
 
113
117
  /** Weeks run along rows (a month) or along columns (the large calendar). */
114
118
  protected abstract readonly weeksRun: "rows" | "columns";
@@ -119,7 +123,7 @@ export abstract class CalendarBase extends LitElement {
119
123
  super();
120
124
  this.value = "";
121
125
  this.selectable = false;
122
- this.range = false;
126
+ this.endPending = false;
123
127
  this.literal = false;
124
128
  this.events = [];
125
129
  }
@@ -139,6 +143,8 @@ export abstract class CalendarBase extends LitElement {
139
143
  protected willUpdate(changed: PropertyValues<this>) {
140
144
  super.willUpdate(changed);
141
145
  if (changed.has("value") && this.value && !readRange(this.value).start) this.value = "";
146
+ // A value given from outside is whole: the next press starts again.
147
+ if (changed.has("value") && this.value !== this.pressedValue) this.endPending = false;
142
148
  }
143
149
 
144
150
  /** Shared words, filled in: `words("Week {week}", { week: 42 })`. */
@@ -177,16 +183,16 @@ export abstract class CalendarBase extends LitElement {
177
183
  /** The chosen days: the first and the last of a range, or the one day twice. */
178
184
  protected get chosen(): { start: Day; end: Day } {
179
185
  const { start, end } = readRange(this.value);
180
- return this.range ? { start, end } : { start, end: start };
186
+ return { start, end: end || start };
181
187
  }
182
188
 
183
189
  /** How a day stands to the selection: an end of it, inside it, or neither; with the end still to come, the pointer previews it. */
184
190
  protected selection(day: Day): { selected: boolean; inside: boolean; first: boolean; last: boolean } {
185
191
  let { start, end } = this.chosen;
186
- if (this.range && start && !end && this.hoverDay) end = this.hoverDay;
192
+ if (this.endPending && start && this.hoverDay) end = this.hoverDay;
187
193
  if (end && end < start) [start, end] = [end, start];
188
194
  const selected = day === start || day === end;
189
- const spans = Boolean(this.range && start && end && start !== end);
195
+ const spans = Boolean(start && end && start !== end);
190
196
  return { selected, inside: spans && day > start && day < end, first: spans && day === start, last: spans && day === end };
191
197
  }
192
198
 
@@ -219,7 +225,10 @@ export abstract class CalendarBase extends LitElement {
219
225
 
220
226
  /**
221
227
  * A person pressed a day: `day-press` fires, and unless it is cancelled a
222
- * selectable calendar takes the day and fires `change`.
228
+ * selectable calendar takes the day and fires `change`. There is one way to
229
+ * pick: the first press chooses a day, which is the value; the next ends a
230
+ * range from it, before or after (the same day again keeps the one day);
231
+ * the press after that starts again.
223
232
  */
224
233
  protected press(day: Day) {
225
234
  const pressed = new CustomEvent<CalendarDayDetail>("day-press", {
@@ -230,18 +239,18 @@ export abstract class CalendarBase extends LitElement {
230
239
  });
231
240
  if (!this.dispatchEvent(pressed) || !this.selectable || !this.allowed(day)) return;
232
241
  const previous = this.value || "";
233
- let value: string = day;
234
- if (this.range) {
235
- // The first press starts a range, the second ends it (either way round); a third starts again.
236
- const { start, end } = readRange(previous);
237
- if (start && !end) value = day < start ? `${day}/${start}` : `${start}/${day}`;
238
- }
242
+ const { start: first } = readRange(previous);
243
+ const ending = this.endPending && Boolean(first);
244
+ const value: string = !ending || day === first ? day : day < first ? `${day}/${first}` : `${first}/${day}`;
239
245
  this.focusDay = day;
240
- if (value === previous) return;
241
- this.value = value;
246
+ this.endPending = !ending;
242
247
  this.hoverDay = undefined;
248
+ this.pressedValue = value;
249
+ // An end on the first day keeps the value but still says the range is whole.
250
+ if (value === previous && !ending) return;
251
+ this.value = value;
243
252
  const { start, end } = readRange(value);
244
- this.dispatchEvent(new CustomEvent<CalendarChangeDetail>("change", { bubbles: true, composed: true, detail: { value, previous, start, end: this.range ? end : start } }));
253
+ this.dispatchEvent(new CustomEvent<CalendarChangeDetail>("change", { bubbles: true, composed: true, detail: { value, previous, start, end: ending ? end || start : "" } }));
245
254
  }
246
255
 
247
256
  protected pressEvent(event: CalendarEvent, day: Day) {
@@ -0,0 +1,67 @@
1
+ // ds-bento.ts
2
+ // Tiles of different sizes, half a square apart: in columns that share the
3
+ // width (a tile `data-span` columns wide, `data-rows` rows high), or with
4
+ // `squares`, each placed on whole squares by its `data-place`, its surface
5
+ // reaching towards the tiles beside it. The tiles are cards (`ds-card`) or
6
+ // any box. Drawn as 1-root/bento.css draws `.ds-bento`, and placed by the
7
+ // helpers it shares with 0-face/bento.ts.
8
+
9
+ import { LitElement, html, unsafeCSS } from "lit";
10
+ import { define } from "../0-face/define";
11
+ import { placeBento } from "../0-face/bento";
12
+ import metrics from "../1-root/component-metrics.css?inline";
13
+ import styles from "./styles/ds-bento.css?inline";
14
+
15
+ export class Bento extends LitElement {
16
+ static properties = {
17
+ columns: { type: Number, reflect: true },
18
+ squares: { type: Boolean, reflect: true },
19
+ };
20
+
21
+ /** The number of columns (flow, three by default), or the squares across (`squares`, 48 by default). */
22
+ declare columns: number | undefined;
23
+ /** Places every tile on whole squares by its `data-place`. */
24
+ declare squares: boolean;
25
+
26
+ static styles = [unsafeCSS(metrics), unsafeCSS(styles)];
27
+
28
+ private observer?: MutationObserver;
29
+
30
+ constructor() {
31
+ super();
32
+ this.squares = false;
33
+ }
34
+
35
+ connectedCallback() {
36
+ super.connectedCallback();
37
+ this.observer = new MutationObserver(() => this.place());
38
+ this.observer.observe(this, { childList: true, subtree: true, attributes: true, attributeFilter: ["data-place", "hidden"] });
39
+ }
40
+
41
+ disconnectedCallback() {
42
+ this.observer?.disconnect();
43
+ super.disconnectedCallback();
44
+ }
45
+
46
+ protected updated() {
47
+ if (this.columns) this.style.setProperty("--ds-bento-columns", String(this.columns));
48
+ else this.style.removeProperty("--ds-bento-columns");
49
+ this.place();
50
+ }
51
+
52
+ private place() {
53
+ if (this.squares) placeBento(this);
54
+ }
55
+
56
+ render() {
57
+ return html`<slot @slotchange=${() => this.place()}></slot>`;
58
+ }
59
+ }
60
+
61
+ define("ds-bento", Bento);
62
+
63
+ declare global {
64
+ interface HTMLElementTagNameMap {
65
+ "ds-bento": Bento;
66
+ }
67
+ }