@timmo001/groundwork-ui 0.9.9 → 0.9.11

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": {
@@ -13839,7 +13855,7 @@
13839
13855
  "text": "Properties"
13840
13856
  },
13841
13857
  "static": true,
13842
- "default": "{ heading: {}, confirm: { type: \"boolean\" }, }",
13858
+ "default": "{ heading: {}, confirm: { type: \"boolean\" }, closeLabel: { attribute: \"close-label\" }, }",
13843
13859
  "inheritedFrom": {
13844
13860
  "name": "GwElement",
13845
13861
  "module": "src/element.ts"
@@ -13874,6 +13890,15 @@
13874
13890
  },
13875
13891
  "default": "false"
13876
13892
  },
13893
+ {
13894
+ "kind": "field",
13895
+ "name": "closeLabel",
13896
+ "type": {
13897
+ "text": "string"
13898
+ },
13899
+ "description": "The close button's name, for an app in another language.",
13900
+ "default": "\"Close\""
13901
+ },
13877
13902
  {
13878
13903
  "kind": "field",
13879
13904
  "name": "returnValue",
@@ -14324,6 +14349,13 @@
14324
14349
  },
14325
14350
  "description": "Makes it an `alertdialog`, for a question that needs an answer.",
14326
14351
  "name": "confirm"
14352
+ },
14353
+ {
14354
+ "type": {
14355
+ "text": "string"
14356
+ },
14357
+ "description": "The close button's name, for an app in another language. Defaults to `Close`.",
14358
+ "name": "close-label"
14327
14359
  }
14328
14360
  ],
14329
14361
  "superclass": {
@@ -16562,23 +16594,64 @@
16562
16594
  },
16563
16595
  {
16564
16596
  "kind": "javascript-module",
16565
- "path": "src/gw-skeleton.ts",
16597
+ "path": "src/gw-sheet.ts",
16566
16598
  "declarations": [
16567
16599
  {
16568
16600
  "kind": "class",
16569
- "description": "A placeholder for what's loading, sized by its container or by `width`\nand `height` in CSS. The shapes below extend it, and components with a\n`loading` attribute draw their own from them, so a spinner is only needed\nfor an action in progress. It's hidden from assistive technology, so mark\nthe region it sits in `aria-busy`.",
16570
- "name": "GwSkeleton",
16601
+ "description": "A panel of filters, settings or details that slides in from an edge of the\nscreen over the page, rather than taking a place in its layout. With\n`side=\"auto\"` it comes from the start edge, full height, and from the\nbottom on narrow screens.\n\nIt's on the Popover API, in the top layer, and closes on Esc, a click\noutside or its close button, whose chevron points the way it leaves. Open\nit with `show()`, or `toggle()` from the button that opens it. Its content\nscrolls under a header with the heading and an `actions` slot, such as a\nClear button.",
16602
+ "name": "GwSheet",
16571
16603
  "cssProperties": [
16572
16604
  {
16573
- "description": "The fill. Defaults to the neutral fill.",
16574
- "name": "--gw-skeleton-color"
16605
+ "description": "Its width from the start or end edge. Defaults to `20rem`.",
16606
+ "name": "--gw-sheet-width"
16575
16607
  },
16576
16608
  {
16577
- "description": "The corners. Defaults to the control radius.",
16578
- "name": "--gw-skeleton-radius"
16609
+ "description": "Its greatest height from the bottom. Defaults to `85vh`.",
16610
+ "name": "--gw-sheet-height"
16611
+ }
16612
+ ],
16613
+ "cssParts": [
16614
+ {
16615
+ "description": "The sheet's surface.",
16616
+ "name": "surface"
16617
+ },
16618
+ {
16619
+ "description": "The row with the close button, heading and actions.",
16620
+ "name": "header"
16621
+ },
16622
+ {
16623
+ "description": "The heading.",
16624
+ "name": "heading"
16625
+ },
16626
+ {
16627
+ "description": "The content.",
16628
+ "name": "body"
16629
+ }
16630
+ ],
16631
+ "slots": [
16632
+ {
16633
+ "description": "The content, which scrolls.",
16634
+ "name": ""
16635
+ },
16636
+ {
16637
+ "description": "Buttons in the header, after the heading.",
16638
+ "name": "actions"
16579
16639
  }
16580
16640
  ],
16581
16641
  "members": [
16642
+ {
16643
+ "kind": "field",
16644
+ "name": "properties",
16645
+ "type": {
16646
+ "text": "Properties"
16647
+ },
16648
+ "static": true,
16649
+ "default": "{ heading: {}, side: { reflect: true }, closeLabel: { attribute: \"close-label\" }, }",
16650
+ "inheritedFrom": {
16651
+ "name": "GwElement",
16652
+ "module": "src/element.ts"
16653
+ }
16654
+ },
16582
16655
  {
16583
16656
  "kind": "field",
16584
16657
  "name": "styles",
@@ -16586,7 +16659,7 @@
16586
16659
  "text": "readonly CSSStyleSheet[]"
16587
16660
  },
16588
16661
  "static": true,
16589
- "default": "[ base, css` :host { display: block; min-height: 1rem; background: var( --gw-skeleton-color, var(--gw-color-fill-neutral-resting) ); border-radius: var(--gw-skeleton-radius, var(--gw-radius-control)); animation: pulse 2s ease-in-out infinite; } @keyframes pulse { 50% { opacity: 0.45; } } @media (forced-colors: active) { :host { background: GrayText; } } `, ]",
16662
+ "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; } `, ]",
16590
16663
  "inheritedFrom": {
16591
16664
  "name": "GwElement",
16592
16665
  "module": "src/element.ts"
@@ -16594,12 +16667,137 @@
16594
16667
  },
16595
16668
  {
16596
16669
  "kind": "field",
16597
- "name": "properties",
16670
+ "name": "heading",
16598
16671
  "type": {
16599
- "text": "Properties"
16672
+ "text": "string"
16600
16673
  },
16601
- "static": true,
16602
- "default": "{}",
16674
+ "default": "\"\""
16675
+ },
16676
+ {
16677
+ "kind": "field",
16678
+ "name": "side",
16679
+ "type": {
16680
+ "text": "\"auto\" | Side"
16681
+ },
16682
+ "default": "\"auto\""
16683
+ },
16684
+ {
16685
+ "kind": "field",
16686
+ "name": "closeLabel",
16687
+ "type": {
16688
+ "text": "string"
16689
+ },
16690
+ "description": "The close button's name, for an app in another language.",
16691
+ "default": "\"Close\""
16692
+ },
16693
+ {
16694
+ "kind": "field",
16695
+ "name": "#heading",
16696
+ "privacy": "private",
16697
+ "readonly": true
16698
+ },
16699
+ {
16700
+ "kind": "field",
16701
+ "name": "#closeButton",
16702
+ "privacy": "private",
16703
+ "readonly": true
16704
+ },
16705
+ {
16706
+ "kind": "field",
16707
+ "name": "#surface",
16708
+ "privacy": "private",
16709
+ "type": {
16710
+ "text": "HTMLDivElement"
16711
+ },
16712
+ "readonly": true
16713
+ },
16714
+ {
16715
+ "kind": "field",
16716
+ "name": "#narrow",
16717
+ "privacy": "private",
16718
+ "readonly": true
16719
+ },
16720
+ {
16721
+ "kind": "field",
16722
+ "name": "#returnFocus",
16723
+ "privacy": "private",
16724
+ "type": {
16725
+ "text": "HTMLElement | undefined"
16726
+ }
16727
+ },
16728
+ {
16729
+ "kind": "field",
16730
+ "name": "open",
16731
+ "description": "Whether it's open.",
16732
+ "readonly": true
16733
+ },
16734
+ {
16735
+ "kind": "method",
16736
+ "name": "show"
16737
+ },
16738
+ {
16739
+ "kind": "method",
16740
+ "name": "close"
16741
+ },
16742
+ {
16743
+ "kind": "method",
16744
+ "name": "toggle",
16745
+ "description": "Opens it, or closes it if it's open, as the button that opens it would."
16746
+ },
16747
+ {
16748
+ "kind": "method",
16749
+ "name": "#edge",
16750
+ "privacy": "private",
16751
+ "return": {
16752
+ "type": {
16753
+ "text": "Side"
16754
+ }
16755
+ },
16756
+ "description": "The edge it comes from now, with `auto` settled by the screen's width."
16757
+ },
16758
+ {
16759
+ "kind": "field",
16760
+ "name": "#place",
16761
+ "privacy": "private",
16762
+ "readonly": true
16763
+ },
16764
+ {
16765
+ "kind": "field",
16766
+ "name": "#onToggle",
16767
+ "privacy": "private",
16768
+ "readonly": true
16769
+ },
16770
+ {
16771
+ "kind": "method",
16772
+ "name": "create",
16773
+ "privacy": "protected",
16774
+ "parameters": [
16775
+ {
16776
+ "name": "root",
16777
+ "type": {
16778
+ "text": "ShadowRoot"
16779
+ }
16780
+ }
16781
+ ],
16782
+ "description": "Builds the shadow root's content once, on first connection.",
16783
+ "inheritedFrom": {
16784
+ "name": "GwElement",
16785
+ "module": "src/element.ts"
16786
+ }
16787
+ },
16788
+ {
16789
+ "kind": "method",
16790
+ "name": "update",
16791
+ "privacy": "protected",
16792
+ "parameters": [
16793
+ {
16794
+ "name": "_changed",
16795
+ "type": {
16796
+ "text": "ReadonlySet<string>"
16797
+ }
16798
+ }
16799
+ ],
16800
+ "description": "Sets what the changed properties affect.",
16603
16801
  "inheritedFrom": {
16604
16802
  "name": "GwElement",
16605
16803
  "module": "src/element.ts"
@@ -16842,42 +17040,6 @@
16842
17040
  "module": "src/element.ts"
16843
17041
  }
16844
17042
  },
16845
- {
16846
- "kind": "method",
16847
- "name": "create",
16848
- "privacy": "protected",
16849
- "parameters": [
16850
- {
16851
- "name": "_root",
16852
- "type": {
16853
- "text": "ShadowRoot | HTMLElement"
16854
- }
16855
- }
16856
- ],
16857
- "description": "Builds the shadow root's content once, on first connection.",
16858
- "inheritedFrom": {
16859
- "name": "GwElement",
16860
- "module": "src/element.ts"
16861
- }
16862
- },
16863
- {
16864
- "kind": "method",
16865
- "name": "update",
16866
- "privacy": "protected",
16867
- "parameters": [
16868
- {
16869
- "name": "_changed",
16870
- "type": {
16871
- "text": "ReadonlySet<string>"
16872
- }
16873
- }
16874
- ],
16875
- "description": "Sets what the changed properties affect.",
16876
- "inheritedFrom": {
16877
- "name": "GwElement",
16878
- "module": "src/element.ts"
16879
- }
16880
- },
16881
17043
  {
16882
17044
  "kind": "method",
16883
17045
  "name": "#flush",
@@ -16917,16 +17079,425 @@
16917
17079
  }
16918
17080
  }
16919
17081
  ],
17082
+ "events": [
17083
+ {
17084
+ "type": {
17085
+ "text": "Event"
17086
+ },
17087
+ "description": "When it closes.",
17088
+ "name": "close"
17089
+ }
17090
+ ],
17091
+ "attributes": [
17092
+ {
17093
+ "type": {
17094
+ "text": "string"
17095
+ },
17096
+ "description": "The heading beside the close button.",
17097
+ "name": "heading"
17098
+ },
17099
+ {
17100
+ "type": {
17101
+ "text": "\"auto\"|\"start\"|\"end\"|\"bottom\""
17102
+ },
17103
+ "description": "The edge it slides in from. `auto` is the start edge, or the bottom on narrow screens. Defaults to `auto`.",
17104
+ "name": "side"
17105
+ },
17106
+ {
17107
+ "type": {
17108
+ "text": "string"
17109
+ },
17110
+ "description": "The close button's name, for an app in another language. Defaults to `Close`.",
17111
+ "name": "close-label"
17112
+ }
17113
+ ],
16920
17114
  "superclass": {
16921
17115
  "name": "GwElement",
16922
17116
  "module": "/src/element.js"
16923
17117
  },
16924
- "tagName": "gw-skeleton",
17118
+ "tagName": "gw-sheet",
16925
17119
  "customElement": true,
16926
- "summary": "A pulsing placeholder for what's loading."
16927
- },
17120
+ "summary": "A panel that slides in from an edge of the screen."
17121
+ }
17122
+ ],
17123
+ "exports": [
16928
17124
  {
16929
- "kind": "class",
17125
+ "kind": "js",
17126
+ "name": "GwSheet",
17127
+ "declaration": {
17128
+ "name": "GwSheet",
17129
+ "module": "src/gw-sheet.ts"
17130
+ }
17131
+ }
17132
+ ]
17133
+ },
17134
+ {
17135
+ "kind": "javascript-module",
17136
+ "path": "src/gw-skeleton.ts",
17137
+ "declarations": [
17138
+ {
17139
+ "kind": "class",
17140
+ "description": "A placeholder for what's loading, sized by its container or by `width`\nand `height` in CSS. The shapes below extend it, and components with a\n`loading` attribute draw their own from them, so a spinner is only needed\nfor an action in progress. It's hidden from assistive technology, so mark\nthe region it sits in `aria-busy`.",
17141
+ "name": "GwSkeleton",
17142
+ "cssProperties": [
17143
+ {
17144
+ "description": "The fill. Defaults to the neutral fill.",
17145
+ "name": "--gw-skeleton-color"
17146
+ },
17147
+ {
17148
+ "description": "The corners. Defaults to the control radius.",
17149
+ "name": "--gw-skeleton-radius"
17150
+ }
17151
+ ],
17152
+ "members": [
17153
+ {
17154
+ "kind": "field",
17155
+ "name": "styles",
17156
+ "type": {
17157
+ "text": "readonly CSSStyleSheet[]"
17158
+ },
17159
+ "static": true,
17160
+ "default": "[ base, css` :host { display: block; min-height: 1rem; background: var( --gw-skeleton-color, var(--gw-color-fill-neutral-resting) ); border-radius: var(--gw-skeleton-radius, var(--gw-radius-control)); animation: pulse 2s ease-in-out infinite; } @keyframes pulse { 50% { opacity: 0.45; } } @media (forced-colors: active) { :host { background: GrayText; } } `, ]",
17161
+ "inheritedFrom": {
17162
+ "name": "GwElement",
17163
+ "module": "src/element.ts"
17164
+ }
17165
+ },
17166
+ {
17167
+ "kind": "field",
17168
+ "name": "properties",
17169
+ "type": {
17170
+ "text": "Properties"
17171
+ },
17172
+ "static": true,
17173
+ "default": "{}",
17174
+ "inheritedFrom": {
17175
+ "name": "GwElement",
17176
+ "module": "src/element.ts"
17177
+ }
17178
+ },
17179
+ {
17180
+ "kind": "field",
17181
+ "name": "shadow",
17182
+ "type": {
17183
+ "text": "boolean"
17184
+ },
17185
+ "static": true,
17186
+ "default": "true",
17187
+ "description": "Whether to attach a shadow root. Light DOM elements set this false.",
17188
+ "inheritedFrom": {
17189
+ "name": "GwElement",
17190
+ "module": "src/element.ts"
17191
+ }
17192
+ },
17193
+ {
17194
+ "kind": "field",
17195
+ "name": "shadowRootOptions",
17196
+ "type": {
17197
+ "text": "ShadowRootInit"
17198
+ },
17199
+ "static": true,
17200
+ "default": "{ mode: \"open\" }",
17201
+ "description": "How the shadow root is attached.",
17202
+ "inheritedFrom": {
17203
+ "name": "GwElement",
17204
+ "module": "src/element.ts"
17205
+ }
17206
+ },
17207
+ {
17208
+ "kind": "method",
17209
+ "name": "define",
17210
+ "static": true,
17211
+ "parameters": [
17212
+ {
17213
+ "name": "tag",
17214
+ "type": {
17215
+ "text": "string"
17216
+ }
17217
+ }
17218
+ ],
17219
+ "description": "Adds the property accessors, then defines the element.",
17220
+ "inheritedFrom": {
17221
+ "name": "GwElement",
17222
+ "module": "src/element.ts"
17223
+ }
17224
+ },
17225
+ {
17226
+ "kind": "field",
17227
+ "name": "#values",
17228
+ "privacy": "private",
17229
+ "readonly": true,
17230
+ "default": "new Map<string, PropertyValue>()",
17231
+ "inheritedFrom": {
17232
+ "name": "GwElement",
17233
+ "module": "src/element.ts"
17234
+ }
17235
+ },
17236
+ {
17237
+ "kind": "field",
17238
+ "name": "#defaults",
17239
+ "privacy": "private",
17240
+ "readonly": true,
17241
+ "default": "new Map<string, PropertyValue>()",
17242
+ "inheritedFrom": {
17243
+ "name": "GwElement",
17244
+ "module": "src/element.ts"
17245
+ }
17246
+ },
17247
+ {
17248
+ "kind": "field",
17249
+ "name": "#changed",
17250
+ "privacy": "private",
17251
+ "readonly": true,
17252
+ "default": "new Set<string>()",
17253
+ "inheritedFrom": {
17254
+ "name": "GwElement",
17255
+ "module": "src/element.ts"
17256
+ }
17257
+ },
17258
+ {
17259
+ "kind": "field",
17260
+ "name": "#unreflected",
17261
+ "privacy": "private",
17262
+ "readonly": true,
17263
+ "default": "new Set<string>()",
17264
+ "inheritedFrom": {
17265
+ "name": "GwElement",
17266
+ "module": "src/element.ts"
17267
+ }
17268
+ },
17269
+ {
17270
+ "kind": "field",
17271
+ "name": "#created",
17272
+ "privacy": "private",
17273
+ "type": {
17274
+ "text": "boolean"
17275
+ },
17276
+ "default": "false",
17277
+ "inheritedFrom": {
17278
+ "name": "GwElement",
17279
+ "module": "src/element.ts"
17280
+ }
17281
+ },
17282
+ {
17283
+ "kind": "field",
17284
+ "name": "#queued",
17285
+ "privacy": "private",
17286
+ "type": {
17287
+ "text": "boolean"
17288
+ },
17289
+ "default": "false",
17290
+ "inheritedFrom": {
17291
+ "name": "GwElement",
17292
+ "module": "src/element.ts"
17293
+ }
17294
+ },
17295
+ {
17296
+ "kind": "field",
17297
+ "name": "#pending",
17298
+ "privacy": "private",
17299
+ "type": {
17300
+ "text": "Promise<void> | undefined"
17301
+ },
17302
+ "inheritedFrom": {
17303
+ "name": "GwElement",
17304
+ "module": "src/element.ts"
17305
+ }
17306
+ },
17307
+ {
17308
+ "kind": "field",
17309
+ "name": "#reflecting",
17310
+ "privacy": "private",
17311
+ "type": {
17312
+ "text": "boolean"
17313
+ },
17314
+ "default": "false",
17315
+ "inheritedFrom": {
17316
+ "name": "GwElement",
17317
+ "module": "src/element.ts"
17318
+ }
17319
+ },
17320
+ {
17321
+ "kind": "field",
17322
+ "name": "#resolveUpdated",
17323
+ "privacy": "private",
17324
+ "type": {
17325
+ "text": "() => void"
17326
+ },
17327
+ "inheritedFrom": {
17328
+ "name": "GwElement",
17329
+ "module": "src/element.ts"
17330
+ }
17331
+ },
17332
+ {
17333
+ "kind": "field",
17334
+ "name": "#updated",
17335
+ "privacy": "private",
17336
+ "readonly": true,
17337
+ "default": "new Promise<void>((resolve) => { this.#resolveUpdated = resolve; })",
17338
+ "inheritedFrom": {
17339
+ "name": "GwElement",
17340
+ "module": "src/element.ts"
17341
+ }
17342
+ },
17343
+ {
17344
+ "kind": "field",
17345
+ "name": "renderRoot",
17346
+ "type": {
17347
+ "text": "ShadowRoot | HTMLElement"
17348
+ },
17349
+ "readonly": true,
17350
+ "description": "The shadow root, or the element itself for a light DOM element.",
17351
+ "inheritedFrom": {
17352
+ "name": "GwElement",
17353
+ "module": "src/element.ts"
17354
+ }
17355
+ },
17356
+ {
17357
+ "kind": "field",
17358
+ "name": "#own",
17359
+ "privacy": "private",
17360
+ "type": {
17361
+ "text": "typeof GwElement"
17362
+ },
17363
+ "readonly": true,
17364
+ "description": "The element's own class, for its static properties and styles.",
17365
+ "default": "new.target",
17366
+ "inheritedFrom": {
17367
+ "name": "GwElement",
17368
+ "module": "src/element.ts"
17369
+ }
17370
+ },
17371
+ {
17372
+ "kind": "field",
17373
+ "name": "updateComplete",
17374
+ "description": "Resolves once the pending update has run, or the first one before it connects.",
17375
+ "readonly": true,
17376
+ "inheritedFrom": {
17377
+ "name": "GwElement",
17378
+ "module": "src/element.ts"
17379
+ }
17380
+ },
17381
+ {
17382
+ "kind": "method",
17383
+ "name": "defaultOf",
17384
+ "privacy": "protected",
17385
+ "parameters": [
17386
+ {
17387
+ "name": "name",
17388
+ "type": {
17389
+ "text": "string"
17390
+ }
17391
+ }
17392
+ ],
17393
+ "description": "The value a property returns to when its attribute is removed. Defaults\nto the value it was given in the constructor.",
17394
+ "inheritedFrom": {
17395
+ "name": "GwElement",
17396
+ "module": "src/element.ts"
17397
+ }
17398
+ },
17399
+ {
17400
+ "kind": "method",
17401
+ "name": "requestUpdate",
17402
+ "parameters": [
17403
+ {
17404
+ "name": "names",
17405
+ "type": {
17406
+ "text": "readonly string[]"
17407
+ }
17408
+ }
17409
+ ],
17410
+ "description": "Asks for an update with the given properties marked as changed.",
17411
+ "inheritedFrom": {
17412
+ "name": "GwElement",
17413
+ "module": "src/element.ts"
17414
+ }
17415
+ },
17416
+ {
17417
+ "kind": "method",
17418
+ "name": "create",
17419
+ "privacy": "protected",
17420
+ "parameters": [
17421
+ {
17422
+ "name": "_root",
17423
+ "type": {
17424
+ "text": "ShadowRoot | HTMLElement"
17425
+ }
17426
+ }
17427
+ ],
17428
+ "description": "Builds the shadow root's content once, on first connection.",
17429
+ "inheritedFrom": {
17430
+ "name": "GwElement",
17431
+ "module": "src/element.ts"
17432
+ }
17433
+ },
17434
+ {
17435
+ "kind": "method",
17436
+ "name": "update",
17437
+ "privacy": "protected",
17438
+ "parameters": [
17439
+ {
17440
+ "name": "_changed",
17441
+ "type": {
17442
+ "text": "ReadonlySet<string>"
17443
+ }
17444
+ }
17445
+ ],
17446
+ "description": "Sets what the changed properties affect.",
17447
+ "inheritedFrom": {
17448
+ "name": "GwElement",
17449
+ "module": "src/element.ts"
17450
+ }
17451
+ },
17452
+ {
17453
+ "kind": "method",
17454
+ "name": "#flush",
17455
+ "privacy": "private",
17456
+ "inheritedFrom": {
17457
+ "name": "GwElement",
17458
+ "module": "src/element.ts"
17459
+ }
17460
+ },
17461
+ {
17462
+ "kind": "method",
17463
+ "name": "#setProperty",
17464
+ "privacy": "private",
17465
+ "parameters": [
17466
+ {
17467
+ "name": "name",
17468
+ "type": {
17469
+ "text": "string"
17470
+ }
17471
+ },
17472
+ {
17473
+ "name": "options",
17474
+ "type": {
17475
+ "text": "PropertyOptions"
17476
+ }
17477
+ },
17478
+ {
17479
+ "name": "value",
17480
+ "type": {
17481
+ "text": "PropertyValue"
17482
+ }
17483
+ }
17484
+ ],
17485
+ "inheritedFrom": {
17486
+ "name": "GwElement",
17487
+ "module": "src/element.ts"
17488
+ }
17489
+ }
17490
+ ],
17491
+ "superclass": {
17492
+ "name": "GwElement",
17493
+ "module": "/src/element.js"
17494
+ },
17495
+ "tagName": "gw-skeleton",
17496
+ "customElement": true,
17497
+ "summary": "A pulsing placeholder for what's loading."
17498
+ },
17499
+ {
17500
+ "kind": "class",
16930
17501
  "description": "A line of text. Its height follows the font size around it, so it lines up\nwith the text it stands in for.",
16931
17502
  "name": "GwSkeletonText",
16932
17503
  "cssProperties": [
@@ -20371,6 +20942,14 @@
20371
20942
  "module": "src/gw-setting-row.js"
20372
20943
  }
20373
20944
  },
20945
+ {
20946
+ "kind": "js",
20947
+ "name": "*",
20948
+ "declaration": {
20949
+ "name": "*",
20950
+ "module": "src/gw-sheet.js"
20951
+ }
20952
+ },
20374
20953
  {
20375
20954
  "kind": "js",
20376
20955
  "name": "*",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timmo001/groundwork-ui",
3
- "version": "0.9.9",
3
+ "version": "0.9.11",
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/events.ts CHANGED
@@ -35,6 +35,9 @@ export interface GwEventMap {
35
35
  "gw-select": {
36
36
  change: Event;
37
37
  };
38
+ "gw-sheet": {
39
+ close: Event;
40
+ };
38
41
  "gw-theme-gallery": {
39
42
  theme: CustomEvent<{ id: string }>;
40
43
  };
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();
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
 
@@ -0,0 +1,328 @@
1
+ /**
2
+ * `gw-sheet`: a panel that slides in from an edge of the screen.
3
+ * Docs: https://groundwork.timmo.dev/components/overlays/#sheet
4
+ *
5
+ * @module
6
+ */
7
+ import { GwElement, css, h, setAttribute } from "./element.js";
8
+ import "./gw-button.js";
9
+ import { base } from "./styles.js";
10
+
11
+ // MDI's chevrons, pointing the way the sheet leaves when it closes
12
+ const chevrons = {
13
+ start: "M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z",
14
+ end: "M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z",
15
+ bottom: "M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z",
16
+ } as const;
17
+
18
+ type Side = keyof typeof chevrons;
19
+
20
+ /** Below this width, `side="auto"` slides up from the bottom. */
21
+ const narrow = "(width < 40rem)";
22
+
23
+ /**
24
+ * A panel of filters, settings or details that slides in from an edge of the
25
+ * screen over the page, rather than taking a place in its layout. With
26
+ * `side="auto"` it comes from the start edge, full height, and from the
27
+ * bottom on narrow screens.
28
+ *
29
+ * It's on the Popover API, in the top layer, and closes on Esc, a click
30
+ * outside or its close button, whose chevron points the way it leaves. Open
31
+ * it with `show()`, or `toggle()` from the button that opens it. Its content
32
+ * scrolls under a header with the heading and an `actions` slot, such as a
33
+ * Clear button.
34
+ *
35
+ * @element gw-sheet
36
+ * @summary A panel that slides in from an edge of the screen.
37
+ *
38
+ * @attr {string} heading - The heading beside the close button.
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`.
41
+ *
42
+ * @slot - The content, which scrolls.
43
+ * @slot actions - Buttons in the header, after the heading.
44
+ *
45
+ * @csspart surface - The sheet's surface.
46
+ * @csspart header - The row with the close button, heading and actions.
47
+ * @csspart heading - The heading.
48
+ * @csspart body - The content.
49
+ *
50
+ * @cssprop --gw-sheet-width - Its width from the start or end edge. Defaults to `20rem`.
51
+ * @cssprop --gw-sheet-height - Its greatest height from the bottom. Defaults to `85vh`.
52
+ *
53
+ * @fires {Event} close - When it closes.
54
+ */
55
+ export class GwSheet extends GwElement {
56
+ static override properties = {
57
+ heading: {},
58
+ side: { reflect: true },
59
+ closeLabel: { attribute: "close-label" },
60
+ } as const;
61
+
62
+ static override styles = [
63
+ base,
64
+ css`
65
+ [popover] {
66
+ position: fixed;
67
+ box-sizing: border-box;
68
+ flex-direction: column;
69
+ padding: 0;
70
+ margin: 0;
71
+ overflow: hidden;
72
+ color: var(--gw-color-text-primary);
73
+ background: var(--gw-color-surface-overlay);
74
+ border: 0 solid var(--gw-color-border-normal);
75
+ box-shadow: var(--gw-shadow-l);
76
+ transition:
77
+ translate var(--gw-motion-normal) var(--gw-motion-easing),
78
+ display var(--gw-motion-normal) allow-discrete,
79
+ overlay var(--gw-motion-normal) allow-discrete;
80
+ }
81
+
82
+ [popover]:popover-open {
83
+ display: flex;
84
+ translate: 0 0;
85
+ }
86
+
87
+ /* The page dims as the sheet slides in, and back as it leaves */
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 {
97
+ background: rgb(0 0 0 / 40%);
98
+ }
99
+
100
+ @starting-style {
101
+ [popover]:popover-open::backdrop {
102
+ background: rgb(0 0 0 / 0%);
103
+ }
104
+ }
105
+
106
+ /* Full height from the start or end edge */
107
+ [data-side="start"],
108
+ [data-side="end"] {
109
+ inset: 0 auto;
110
+ width: min(var(--gw-sheet-width, 20rem), 100vw);
111
+ height: 100%;
112
+ max-height: none;
113
+ }
114
+
115
+ [data-side="start"] {
116
+ inset-inline-start: 0;
117
+ border-inline-end-width: max(var(--gw-border-width), 1px);
118
+ translate: -100% 0;
119
+ }
120
+
121
+ [data-side="end"] {
122
+ inset-inline-end: 0;
123
+ border-inline-start-width: max(var(--gw-border-width), 1px);
124
+ translate: 100% 0;
125
+ }
126
+
127
+ :host(:dir(rtl)) [data-side="start"] {
128
+ translate: 100% 0;
129
+ }
130
+
131
+ :host(:dir(rtl)) [data-side="end"] {
132
+ translate: -100% 0;
133
+ }
134
+
135
+ /* Up from the bottom, with room for the phone's home bar */
136
+ [data-side="bottom"] {
137
+ inset: auto 0 0;
138
+ width: 100%;
139
+ max-width: none;
140
+ max-height: var(--gw-sheet-height, 85vh);
141
+ padding-block-end: env(safe-area-inset-bottom);
142
+ border-block-start-width: max(var(--gw-border-width), 1px);
143
+ border-start-start-radius: var(--gw-radius-panel);
144
+ border-start-end-radius: var(--gw-radius-panel);
145
+ translate: 0 100%;
146
+ }
147
+
148
+ @starting-style {
149
+ [data-side="start"]:popover-open {
150
+ translate: -100% 0;
151
+ }
152
+
153
+ [data-side="end"]:popover-open {
154
+ translate: 100% 0;
155
+ }
156
+
157
+ [data-side="bottom"]:popover-open {
158
+ translate: 0 100%;
159
+ }
160
+ }
161
+
162
+ header {
163
+ display: flex;
164
+ flex: none;
165
+ gap: var(--gw-space-2);
166
+ align-items: center;
167
+ padding: var(--gw-space-2) var(--gw-space-3);
168
+ border-block-end: var(--gw-border-width) solid
169
+ var(--gw-color-border-quiet);
170
+ }
171
+
172
+ h2 {
173
+ flex: 1;
174
+ min-width: 0;
175
+ margin: 0;
176
+ font-size: var(--gw-font-size-m);
177
+ font-weight: var(--gw-font-weight-strong);
178
+ color: var(--gw-color-text-strong);
179
+ }
180
+
181
+ .body {
182
+ display: flex;
183
+ flex: 1;
184
+ flex-direction: column;
185
+ gap: var(--gw-space-3);
186
+ min-height: 0;
187
+ padding: var(--gw-space-4);
188
+ /* It scrolls down only, so content reaching into its padding never
189
+ makes it scroll sideways */
190
+ overflow: hidden auto;
191
+ overscroll-behavior: contain;
192
+ scrollbar-width: thin;
193
+ }
194
+ `,
195
+ ];
196
+
197
+ declare heading: string;
198
+ declare side: "auto" | Side;
199
+ /** The close button's name, for an app in another language. */
200
+ declare closeLabel: string;
201
+
202
+ readonly #heading = h("h2", { id: "heading", part: "heading" });
203
+
204
+ readonly #closeButton = h(
205
+ "gw-button",
206
+ {
207
+ variant: "neutral",
208
+ appearance: "plain",
209
+ size: "small",
210
+ labels: "hide",
211
+ },
212
+ "Close",
213
+ );
214
+
215
+ readonly #surface: HTMLDivElement = h(
216
+ "div",
217
+ {
218
+ popover: "auto",
219
+ role: "dialog",
220
+ tabindex: "-1",
221
+ part: "surface",
222
+ },
223
+ h(
224
+ "header",
225
+ { part: "header" },
226
+ this.#closeButton,
227
+ this.#heading,
228
+ h("slot", { name: "actions" }),
229
+ ),
230
+ h("div", { class: "body", part: "body" }, h("slot")),
231
+ );
232
+
233
+ readonly #narrow = window.matchMedia(narrow);
234
+
235
+ // Where focus was before it opened, to go back to when it closes
236
+ #returnFocus: HTMLElement | undefined;
237
+
238
+ constructor() {
239
+ super();
240
+ this.heading = "";
241
+ this.side = "auto";
242
+ this.closeLabel = "Close";
243
+ }
244
+
245
+ /** Whether it's open. */
246
+ get open() {
247
+ return this.#surface.matches(":popover-open");
248
+ }
249
+
250
+ show() {
251
+ if (this.open) return;
252
+
253
+ const active = document.activeElement;
254
+
255
+ this.#returnFocus = active instanceof HTMLElement ? active : undefined;
256
+ this.#place();
257
+ this.#surface.showPopover();
258
+ }
259
+
260
+ close() {
261
+ if (this.open) this.#surface.hidePopover();
262
+ }
263
+
264
+ /** Opens it, or closes it if it's open, as the button that opens it would. */
265
+ toggle() {
266
+ if (this.open) this.close();
267
+ else this.show();
268
+ }
269
+
270
+ /** The edge it comes from now, with `auto` settled by the screen's width. */
271
+ #edge(): Side {
272
+ if (this.side !== "auto") return this.side;
273
+
274
+ return this.#narrow.matches ? "bottom" : "start";
275
+ }
276
+
277
+ readonly #place = () => {
278
+ const edge = this.#edge();
279
+
280
+ this.#surface.dataset.side = edge;
281
+ this.#closeButton.icon = chevrons[edge];
282
+ };
283
+
284
+ readonly #onToggle = (event: ToggleEvent) => {
285
+ if (event.newState === "open") {
286
+ this.#narrow.addEventListener("change", this.#place);
287
+ this.#closeButton.focus();
288
+
289
+ return;
290
+ }
291
+
292
+ this.#narrow.removeEventListener("change", this.#place);
293
+ this.#returnFocus?.focus();
294
+ this.#returnFocus = undefined;
295
+ this.dispatchEvent(new Event("close", { bubbles: true, composed: true }));
296
+ };
297
+
298
+ override disconnectedCallback() {
299
+ this.#narrow.removeEventListener("change", this.#place);
300
+ super.disconnectedCallback();
301
+ }
302
+
303
+ protected override create(root: ShadowRoot) {
304
+ this.#surface.addEventListener("toggle", this.#onToggle);
305
+ this.#closeButton.addEventListener("click", () => this.close());
306
+ root.append(this.#surface);
307
+ }
308
+
309
+ protected override update() {
310
+ this.#heading.textContent = this.heading;
311
+ setAttribute(
312
+ this.#surface,
313
+ "aria-labelledby",
314
+ this.heading ? "heading" : null,
315
+ );
316
+ this.#heading.hidden = !this.heading;
317
+ this.#closeButton.textContent = this.closeLabel;
318
+ this.#place();
319
+ }
320
+ }
321
+
322
+ GwSheet.define("gw-sheet");
323
+
324
+ declare global {
325
+ interface HTMLElementTagNameMap {
326
+ "gw-sheet": GwSheet;
327
+ }
328
+ }
package/src/index.ts CHANGED
@@ -73,6 +73,8 @@ export * from "./gw-select.js";
73
73
 
74
74
  export * from "./gw-setting-row.js";
75
75
 
76
+ export * from "./gw-sheet.js";
77
+
76
78
  export * from "./gw-skeleton.js";
77
79
 
78
80
  export * from "./gw-spinner.js";