@timmo001/groundwork-ui 0.9.10 → 0.9.12

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.
@@ -6401,7 +6401,7 @@
6401
6401
  "text": "Properties"
6402
6402
  },
6403
6403
  "static": true,
6404
- "default": "{ heading: {}, open: { type: \"boolean\", reflect: true }, }",
6404
+ "default": "{ heading: {}, open: { type: \"boolean\", reflect: true }, closeLabel: { attribute: \"close-label\" }, }",
6405
6405
  "inheritedFrom": {
6406
6406
  "name": "GwElement",
6407
6407
  "module": "src/element.ts"
@@ -6436,6 +6436,15 @@
6436
6436
  },
6437
6437
  "default": "false"
6438
6438
  },
6439
+ {
6440
+ "kind": "field",
6441
+ "name": "closeLabel",
6442
+ "type": {
6443
+ "text": "string"
6444
+ },
6445
+ "description": "The close button's name, for an app in another language.",
6446
+ "default": "\"Close\""
6447
+ },
6439
6448
  {
6440
6449
  "kind": "field",
6441
6450
  "name": "returnValue",
@@ -6854,6 +6863,13 @@
6854
6863
  },
6855
6864
  "description": "Whether it shows.",
6856
6865
  "name": "open"
6866
+ },
6867
+ {
6868
+ "type": {
6869
+ "text": "string"
6870
+ },
6871
+ "description": "The close button's name, for an app in another language. Defaults to `Close`.",
6872
+ "name": "close-label"
6857
6873
  }
6858
6874
  ],
6859
6875
  "superclass": {
@@ -13337,7 +13353,7 @@
13337
13353
  "declarations": [
13338
13354
  {
13339
13355
  "kind": "class",
13340
- "description": "A page's title, with an optional icon tile and description, and its\ncontrols at the top right in the `actions` slot.",
13356
+ "description": "A page's title, with an optional icon tile and description, a back button\nor similar in the `start` slot, and its controls at the top right in the\n`actions` slot.",
13341
13357
  "name": "GwPageHeader",
13342
13358
  "cssParts": [
13343
13359
  {
@@ -13354,6 +13370,10 @@
13354
13370
  }
13355
13371
  ],
13356
13372
  "slots": [
13373
+ {
13374
+ "description": "Before the icon tile, such as a back button.",
13375
+ "name": "start"
13376
+ },
13357
13377
  {
13358
13378
  "description": "The page's controls.",
13359
13379
  "name": "actions"
@@ -13380,7 +13400,7 @@
13380
13400
  "text": "readonly CSSStyleSheet[]"
13381
13401
  },
13382
13402
  "static": true,
13383
- "default": "[ base, css` :host { display: flex; gap: var(--gw-space-3); align-items: flex-start; } .tile { display: grid; flex: none; place-items: center; width: 3rem; height: 3rem; color: var(--gw-color-accent); background: var(--gw-color-fill-accent-quiet); border-radius: var(--gw-radius-l); --gw-icon-size: 1.625rem; } .titles { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-0-5); min-width: 0; } h1 { margin: 0; font-size: var(--gw-font-size-2xl); font-weight: var(--gw-font-weight-strong); line-height: 1.2; color: var(--gw-color-text-strong); } p { margin: 0; font-size: var(--gw-font-size-s); color: var(--gw-color-text-secondary); } .actions { display: flex; flex: none; gap: var(--gw-space-1); align-items: center; margin-inline-start: auto; } `, ]",
13403
+ "default": "[ base, css` :host { display: flex; gap: var(--gw-space-3); align-items: flex-start; } .start { display: flex; flex: none; align-items: center; min-height: 3rem; } .start[hidden] { display: none; } .tile { display: grid; flex: none; place-items: center; width: 3rem; height: 3rem; color: var(--gw-color-accent); background: var(--gw-color-fill-accent-quiet); border-radius: var(--gw-radius-l); --gw-icon-size: 1.625rem; } .titles { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-0-5); min-width: 0; } h1 { margin: 0; font-size: var(--gw-font-size-2xl); font-weight: var(--gw-font-weight-strong); line-height: 1.2; color: var(--gw-color-text-strong); } p { margin: 0; font-size: var(--gw-font-size-s); color: var(--gw-color-text-secondary); } .actions { display: flex; flex: none; gap: var(--gw-space-1); align-items: center; margin-inline-start: auto; } /* Centered on the tile when there is one */ .tile:not([hidden]) ~ .actions { min-height: 3rem; } `, ]",
13384
13404
  "inheritedFrom": {
13385
13405
  "name": "GwElement",
13386
13406
  "module": "src/element.ts"
@@ -13416,6 +13436,18 @@
13416
13436
  "privacy": "private",
13417
13437
  "readonly": true
13418
13438
  },
13439
+ {
13440
+ "kind": "field",
13441
+ "name": "#startSlot",
13442
+ "privacy": "private",
13443
+ "readonly": true
13444
+ },
13445
+ {
13446
+ "kind": "field",
13447
+ "name": "#start",
13448
+ "privacy": "private",
13449
+ "readonly": true
13450
+ },
13419
13451
  {
13420
13452
  "kind": "field",
13421
13453
  "name": "#tile",
@@ -13839,7 +13871,7 @@
13839
13871
  "text": "Properties"
13840
13872
  },
13841
13873
  "static": true,
13842
- "default": "{ heading: {}, confirm: { type: \"boolean\" }, }",
13874
+ "default": "{ heading: {}, confirm: { type: \"boolean\" }, closeLabel: { attribute: \"close-label\" }, }",
13843
13875
  "inheritedFrom": {
13844
13876
  "name": "GwElement",
13845
13877
  "module": "src/element.ts"
@@ -13874,6 +13906,15 @@
13874
13906
  },
13875
13907
  "default": "false"
13876
13908
  },
13909
+ {
13910
+ "kind": "field",
13911
+ "name": "closeLabel",
13912
+ "type": {
13913
+ "text": "string"
13914
+ },
13915
+ "description": "The close button's name, for an app in another language.",
13916
+ "default": "\"Close\""
13917
+ },
13877
13918
  {
13878
13919
  "kind": "field",
13879
13920
  "name": "returnValue",
@@ -14324,6 +14365,13 @@
14324
14365
  },
14325
14366
  "description": "Makes it an `alertdialog`, for a question that needs an answer.",
14326
14367
  "name": "confirm"
14368
+ },
14369
+ {
14370
+ "type": {
14371
+ "text": "string"
14372
+ },
14373
+ "description": "The close button's name, for an app in another language. Defaults to `Close`.",
14374
+ "name": "close-label"
14327
14375
  }
14328
14376
  ],
14329
14377
  "superclass": {
@@ -16614,7 +16662,7 @@
16614
16662
  "text": "Properties"
16615
16663
  },
16616
16664
  "static": true,
16617
- "default": "{ heading: {}, side: { reflect: true }, }",
16665
+ "default": "{ heading: {}, side: { reflect: true }, closeLabel: { attribute: \"close-label\" }, }",
16618
16666
  "inheritedFrom": {
16619
16667
  "name": "GwElement",
16620
16668
  "module": "src/element.ts"
@@ -16627,7 +16675,7 @@
16627
16675
  "text": "readonly CSSStyleSheet[]"
16628
16676
  },
16629
16677
  "static": true,
16630
- "default": "[ base, css` [popover] { position: fixed; box-sizing: border-box; flex-direction: column; padding: 0; margin: 0; overflow: hidden; color: var(--gw-color-text-primary); background: var(--gw-color-surface-overlay); border: 0 solid var(--gw-color-border-normal); box-shadow: var(--gw-shadow-l); transition: translate var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open { display: flex; translate: 0 0; } [popover]::backdrop { background: rgb(0 0 0 / 40%); } /* Full height from the start or end edge */ [data-side=\"start\"], [data-side=\"end\"] { inset: 0 auto; width: min(var(--gw-sheet-width, 20rem), 100vw); height: 100%; max-height: none; } [data-side=\"start\"] { inset-inline-start: 0; border-inline-end-width: max(var(--gw-border-width), 1px); translate: -100% 0; } [data-side=\"end\"] { inset-inline-end: 0; border-inline-start-width: max(var(--gw-border-width), 1px); translate: 100% 0; } :host(:dir(rtl)) [data-side=\"start\"] { translate: 100% 0; } :host(:dir(rtl)) [data-side=\"end\"] { translate: -100% 0; } /* Up from the bottom, with room for the phone's home bar */ [data-side=\"bottom\"] { inset: auto 0 0; width: 100%; max-width: none; max-height: var(--gw-sheet-height, 85vh); padding-block-end: env(safe-area-inset-bottom); border-block-start-width: max(var(--gw-border-width), 1px); border-start-start-radius: var(--gw-radius-panel); border-start-end-radius: var(--gw-radius-panel); translate: 0 100%; } @starting-style { [data-side=\"start\"]:popover-open { translate: -100% 0; } [data-side=\"end\"]:popover-open { translate: 100% 0; } [data-side=\"bottom\"]:popover-open { translate: 0 100%; } } header { display: flex; flex: none; gap: var(--gw-space-2); align-items: center; padding: var(--gw-space-2) var(--gw-space-3); border-block-end: var(--gw-border-width) solid var(--gw-color-border-quiet); } h2 { flex: 1; min-width: 0; margin: 0; font-size: var(--gw-font-size-m); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } .body { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-3); min-height: 0; padding: var(--gw-space-4); /* It scrolls down only, so content reaching into its padding never makes it scroll sideways */ overflow: hidden auto; overscroll-behavior: contain; scrollbar-width: thin; } `, ]",
16678
+ "default": "[ base, css` [popover] { position: fixed; box-sizing: border-box; flex-direction: column; padding: 0; margin: 0; overflow: hidden; color: var(--gw-color-text-primary); background: var(--gw-color-surface-overlay); border: 0 solid var(--gw-color-border-normal); box-shadow: var(--gw-shadow-l); transition: translate var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open { display: flex; translate: 0 0; } /* The page dims as the sheet slides in, and back as it leaves */ [popover]::backdrop { background: rgb(0 0 0 / 0%); transition: background-color var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open::backdrop { background: rgb(0 0 0 / 40%); } @starting-style { [popover]:popover-open::backdrop { background: rgb(0 0 0 / 0%); } } /* Full height from the start or end edge */ [data-side=\"start\"], [data-side=\"end\"] { inset: 0 auto; width: min(var(--gw-sheet-width, 20rem), 100vw); height: 100%; max-height: none; } [data-side=\"start\"] { inset-inline-start: 0; border-inline-end-width: max(var(--gw-border-width), 1px); translate: -100% 0; } [data-side=\"end\"] { inset-inline-end: 0; border-inline-start-width: max(var(--gw-border-width), 1px); translate: 100% 0; } :host(:dir(rtl)) [data-side=\"start\"] { translate: 100% 0; } :host(:dir(rtl)) [data-side=\"end\"] { translate: -100% 0; } /* Up from the bottom, with room for the phone's home bar */ [data-side=\"bottom\"] { inset: auto 0 0; width: 100%; max-width: none; max-height: var(--gw-sheet-height, 85vh); padding-block-end: env(safe-area-inset-bottom); border-block-start-width: max(var(--gw-border-width), 1px); border-start-start-radius: var(--gw-radius-panel); border-start-end-radius: var(--gw-radius-panel); translate: 0 100%; } @starting-style { [data-side=\"start\"]:popover-open { translate: -100% 0; } [data-side=\"end\"]:popover-open { translate: 100% 0; } [data-side=\"bottom\"]:popover-open { translate: 0 100%; } } header { display: flex; flex: none; gap: var(--gw-space-2); align-items: center; padding: var(--gw-space-2) var(--gw-space-3); border-block-end: var(--gw-border-width) solid var(--gw-color-border-quiet); } h2 { flex: 1; min-width: 0; margin: 0; font-size: var(--gw-font-size-m); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } .body { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-3); min-height: 0; padding: var(--gw-space-4); /* It scrolls down only, so content reaching into its padding never makes it scroll sideways */ overflow: hidden auto; overscroll-behavior: contain; scrollbar-width: thin; } `, ]",
16631
16679
  "inheritedFrom": {
16632
16680
  "name": "GwElement",
16633
16681
  "module": "src/element.ts"
@@ -16649,6 +16697,15 @@
16649
16697
  },
16650
16698
  "default": "\"auto\""
16651
16699
  },
16700
+ {
16701
+ "kind": "field",
16702
+ "name": "closeLabel",
16703
+ "type": {
16704
+ "text": "string"
16705
+ },
16706
+ "description": "The close button's name, for an app in another language.",
16707
+ "default": "\"Close\""
16708
+ },
16652
16709
  {
16653
16710
  "kind": "field",
16654
16711
  "name": "#heading",
@@ -17061,6 +17118,13 @@
17061
17118
  },
17062
17119
  "description": "The edge it slides in from. `auto` is the start edge, or the bottom on narrow screens. Defaults to `auto`.",
17063
17120
  "name": "side"
17121
+ },
17122
+ {
17123
+ "type": {
17124
+ "text": "string"
17125
+ },
17126
+ "description": "The close button's name, for an app in another language. Defaults to `Close`.",
17127
+ "name": "close-label"
17064
17128
  }
17065
17129
  ],
17066
17130
  "superclass": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timmo001/groundwork-ui",
3
- "version": "0.9.10",
3
+ "version": "0.9.12",
4
4
  "description": "Groundwork's framework-free web components, styled by the --gw-* tokens",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
package/src/gw-dialog.ts CHANGED
@@ -24,6 +24,7 @@ const closeIcon =
24
24
  *
25
25
  * @attr {string} heading - The heading beside the close button.
26
26
  * @attr {boolean} open - Whether it shows.
27
+ * @attr {string} close-label - The close button's name, for an app in another language. Defaults to `Close`.
27
28
  *
28
29
  * @slot - The content.
29
30
  * @slot actions - Buttons; one with a `value` closes it with that value.
@@ -40,6 +41,7 @@ export class GwDialog extends GwElement {
40
41
  static override properties = {
41
42
  heading: {},
42
43
  open: { type: "boolean", reflect: true },
44
+ closeLabel: { attribute: "close-label" },
43
45
  } as const;
44
46
 
45
47
  static override styles = [
@@ -135,6 +137,8 @@ export class GwDialog extends GwElement {
135
137
 
136
138
  declare heading: string;
137
139
  declare open: boolean;
140
+ /** The close button's name, for an app in another language. */
141
+ declare closeLabel: string;
138
142
 
139
143
  /** The value of the action that closed the dialog, or "" for Esc. */
140
144
  returnValue = "";
@@ -143,6 +147,7 @@ export class GwDialog extends GwElement {
143
147
  super();
144
148
  this.heading = "";
145
149
  this.open = false;
150
+ this.closeLabel = "Close";
146
151
  }
147
152
 
148
153
  readonly #heading = h("h2", { id: "heading", part: "heading" });
@@ -236,6 +241,7 @@ export class GwDialog extends GwElement {
236
241
  const dialog = this.#dialog;
237
242
 
238
243
  this.#heading.textContent = this.heading;
244
+ this.#closeButton.textContent = this.closeLabel;
239
245
 
240
246
  if (this.open && !dialog.open) {
241
247
  dialog.showModal();
@@ -8,8 +8,9 @@ import { GwElement, css, h } from "./element.js";
8
8
  import { base, createIcon, setIconPath } from "./styles.js";
9
9
 
10
10
  /**
11
- * A page's title, with an optional icon tile and description, and its
12
- * controls at the top right in the `actions` slot.
11
+ * A page's title, with an optional icon tile and description, a back button
12
+ * or similar in the `start` slot, and its controls at the top right in the
13
+ * `actions` slot.
13
14
  *
14
15
  * @element gw-page-header
15
16
  * @summary A page's title, icon, description and actions.
@@ -18,6 +19,7 @@ import { base, createIcon, setIconPath } from "./styles.js";
18
19
  * @attr {string} description - A line under the title.
19
20
  * @attr {string} icon - SVG path data for the icon tile.
20
21
  *
22
+ * @slot start - Before the icon tile, such as a back button.
21
23
  * @slot actions - The page's controls.
22
24
  *
23
25
  * @csspart tile - The icon tile.
@@ -40,6 +42,17 @@ export class GwPageHeader extends GwElement {
40
42
  align-items: flex-start;
41
43
  }
42
44
 
45
+ .start {
46
+ display: flex;
47
+ flex: none;
48
+ align-items: center;
49
+ min-height: 3rem;
50
+ }
51
+
52
+ .start[hidden] {
53
+ display: none;
54
+ }
55
+
43
56
  .tile {
44
57
  display: grid;
45
58
  flex: none;
@@ -81,6 +94,11 @@ export class GwPageHeader extends GwElement {
81
94
  align-items: center;
82
95
  margin-inline-start: auto;
83
96
  }
97
+
98
+ /* Centered on the tile when there is one */
99
+ .tile:not([hidden]) ~ .actions {
100
+ min-height: 3rem;
101
+ }
84
102
  `,
85
103
  ];
86
104
 
@@ -90,6 +108,11 @@ export class GwPageHeader extends GwElement {
90
108
 
91
109
  readonly #icon = createIcon("");
92
110
 
111
+ readonly #startSlot = h("slot", { name: "start" });
112
+
113
+ // Centered on the tile's height, and out of the row while it's empty
114
+ readonly #start = h("div", { class: "start", hidden: true }, this.#startSlot);
115
+
93
116
  readonly #tile = h("span", { class: "tile", part: "tile" }, this.#icon);
94
117
 
95
118
  readonly #heading = h("h1", { part: "heading" });
@@ -104,7 +127,11 @@ export class GwPageHeader extends GwElement {
104
127
  }
105
128
 
106
129
  protected override create(root: ShadowRoot) {
130
+ this.#startSlot.addEventListener("slotchange", () => {
131
+ this.#start.hidden = this.#startSlot.assignedElements().length === 0;
132
+ });
107
133
  root.append(
134
+ this.#start,
108
135
  this.#tile,
109
136
  h("div", { class: "titles" }, this.#heading, this.#description),
110
137
  h("div", { class: "actions" }, h("slot", { name: "actions" })),
package/src/gw-popover.ts CHANGED
@@ -34,6 +34,7 @@ const sheetBelow = 600;
34
34
  *
35
35
  * @attr {string} heading - The heading beside the close button.
36
36
  * @attr {boolean} confirm - Makes it an `alertdialog`, for a question that needs an answer.
37
+ * @attr {string} close-label - The close button's name, for an app in another language. Defaults to `Close`.
37
38
  *
38
39
  * @slot - The content.
39
40
  * @slot actions - Buttons; one with a `value` closes it with that value.
@@ -51,6 +52,7 @@ export class GwPopover extends GwElement {
51
52
  static override properties = {
52
53
  heading: {},
53
54
  confirm: { type: "boolean" },
55
+ closeLabel: { attribute: "close-label" },
54
56
  } as const;
55
57
 
56
58
  static override styles = [
@@ -188,6 +190,8 @@ export class GwPopover extends GwElement {
188
190
 
189
191
  declare heading: string;
190
192
  declare confirm: boolean;
193
+ /** The close button's name, for an app in another language. */
194
+ declare closeLabel: string;
191
195
 
192
196
  /** The value of the action that closed it, or "" for Esc or a click outside. */
193
197
  returnValue = "";
@@ -242,6 +246,7 @@ export class GwPopover extends GwElement {
242
246
  super();
243
247
  this.heading = "";
244
248
  this.confirm = false;
249
+ this.closeLabel = "Close";
245
250
  }
246
251
 
247
252
  /** Whether its content has changed since it opened, so closing it asks first. */
@@ -382,6 +387,7 @@ export class GwPopover extends GwElement {
382
387
  setAttribute(popover, "aria-describedby", this.heading ? "body" : null);
383
388
  this.#heading.textContent = this.heading;
384
389
  this.#heading.hidden = !this.heading;
390
+ this.#closeButton.textContent = this.closeLabel;
385
391
  }
386
392
  }
387
393
 
@@ -12,6 +12,8 @@ export interface Segment {
12
12
  readonly label: string;
13
13
  /** SVG path data for an icon beside, or instead of, the label. */
14
14
  readonly icon?: string;
15
+ /** A longer description, shown as the option's tooltip. */
16
+ readonly hint?: string;
15
17
  }
16
18
 
17
19
  /**
@@ -297,7 +299,8 @@ export class GwSegmented extends GwElement {
297
299
  setAttribute(
298
300
  label,
299
301
  "title",
300
- option.icon && this.labels !== "show" ? option.label : null,
302
+ option.hint ??
303
+ (option.icon && this.labels !== "show" ? option.label : null),
301
304
  );
302
305
  }
303
306
 
package/src/gw-sheet.ts CHANGED
@@ -37,6 +37,7 @@ const narrow = "(width < 40rem)";
37
37
  *
38
38
  * @attr {string} heading - The heading beside the close button.
39
39
  * @attr {"auto"|"start"|"end"|"bottom"} side - The edge it slides in from. `auto` is the start edge, or the bottom on narrow screens. Defaults to `auto`.
40
+ * @attr {string} close-label - The close button's name, for an app in another language. Defaults to `Close`.
40
41
  *
41
42
  * @slot - The content, which scrolls.
42
43
  * @slot actions - Buttons in the header, after the heading.
@@ -55,6 +56,7 @@ export class GwSheet extends GwElement {
55
56
  static override properties = {
56
57
  heading: {},
57
58
  side: { reflect: true },
59
+ closeLabel: { attribute: "close-label" },
58
60
  } as const;
59
61
 
60
62
  static override styles = [
@@ -82,10 +84,25 @@ export class GwSheet extends GwElement {
82
84
  translate: 0 0;
83
85
  }
84
86
 
87
+ /* The page dims as the sheet slides in, and back as it leaves */
85
88
  [popover]::backdrop {
89
+ background: rgb(0 0 0 / 0%);
90
+ transition:
91
+ background-color var(--gw-motion-normal) var(--gw-motion-easing),
92
+ display var(--gw-motion-normal) allow-discrete,
93
+ overlay var(--gw-motion-normal) allow-discrete;
94
+ }
95
+
96
+ [popover]:popover-open::backdrop {
86
97
  background: rgb(0 0 0 / 40%);
87
98
  }
88
99
 
100
+ @starting-style {
101
+ [popover]:popover-open::backdrop {
102
+ background: rgb(0 0 0 / 0%);
103
+ }
104
+ }
105
+
89
106
  /* Full height from the start or end edge */
90
107
  [data-side="start"],
91
108
  [data-side="end"] {
@@ -179,6 +196,8 @@ export class GwSheet extends GwElement {
179
196
 
180
197
  declare heading: string;
181
198
  declare side: "auto" | Side;
199
+ /** The close button's name, for an app in another language. */
200
+ declare closeLabel: string;
182
201
 
183
202
  readonly #heading = h("h2", { id: "heading", part: "heading" });
184
203
 
@@ -220,6 +239,7 @@ export class GwSheet extends GwElement {
220
239
  super();
221
240
  this.heading = "";
222
241
  this.side = "auto";
242
+ this.closeLabel = "Close";
223
243
  }
224
244
 
225
245
  /** Whether it's open. */
@@ -294,6 +314,7 @@ export class GwSheet extends GwElement {
294
314
  this.heading ? "heading" : null,
295
315
  );
296
316
  this.#heading.hidden = !this.heading;
317
+ this.#closeButton.textContent = this.closeLabel;
297
318
  this.#place();
298
319
  }
299
320
  }