@inglorious/ui 9.7.2 → 9.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/ui",
3
- "version": "9.7.2",
3
+ "version": "9.8.0",
4
4
  "description": "A design system for Inglorious Web with render-function components and customizable themes",
5
5
  "author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
@@ -1,5 +1,10 @@
1
1
  .iw-switch {
2
2
  --iw-switch-active-bg: var(--iw-color-primary);
3
+ /* Contains the visually hidden input below. Without it, the input is placed
4
+ against the nearest positioned ancestor, possibly outside the scroll
5
+ container the switch sits in, and focusing it on toggle scrolls that
6
+ ancestor, even one with overflow: hidden, to bring the input into view. */
7
+ position: relative;
3
8
  display: inline-flex;
4
9
  align-items: center;
5
10
  gap: var(--iw-space-2);
@@ -14,7 +19,10 @@
14
19
  pointer-events: none;
15
20
  }
16
21
 
22
+ /* Fixed size: a long label shrinks the text, never the track, which would
23
+ otherwise narrow until it is as round as a radio button. */
17
24
  .iw-switch-track {
25
+ flex-shrink: 0;
18
26
  width: 2.25rem;
19
27
  height: 1.25rem;
20
28
  border-radius: 999px;
@@ -47,6 +55,7 @@
47
55
  .iw-switch-text {
48
56
  display: flex;
49
57
  flex-direction: column;
58
+ min-width: 0;
50
59
  }
51
60
 
52
61
  .iw-switch-description {
@@ -10,9 +10,11 @@ export default {
10
10
  argTypes: {
11
11
  isOpen: { control: "boolean" },
12
12
  isModal: { control: "boolean" },
13
+ variant: { control: "select", options: ["dialog", "sheet"] },
13
14
  size: { control: "select", options: ["sm", "md", "lg"] },
14
15
  title: { control: "text" },
15
16
  description: { control: "text" },
17
+ closeLabel: { control: "text" },
16
18
  onClose: { action: "onClose" },
17
19
  },
18
20
  }
@@ -56,3 +58,10 @@ export const Modal = {
56
58
  tags: ["!autodocs"],
57
59
  args: { ...Open.args, isModal: true },
58
60
  }
61
+
62
+ // A bottom sheet below 48rem: shrink the viewport to see it rise from the
63
+ // bottom edge. Modal, like Modal, so it stays off the docs page.
64
+ export const Sheet = {
65
+ tags: ["!autodocs"],
66
+ args: { ...Open.args, isModal: true, variant: "sheet" },
67
+ }
@@ -47,21 +47,9 @@
47
47
  font-size: 1.1rem;
48
48
  }
49
49
 
50
+ /* The title takes the room, the close button only its own. */
50
51
  .iw-dialog-close {
51
- border: 0 none;
52
- background: transparent;
53
- width: 1.75rem;
54
- height: 1.75rem;
55
- border-radius: var(--iw-radius-sm);
56
- display: inline-flex;
57
- align-items: center;
58
- justify-content: center;
59
- cursor: pointer;
60
- color: inherit;
61
- }
62
-
63
- .iw-dialog-close:hover {
64
- background-color: color-mix(in srgb, currentColor 12%, transparent);
52
+ flex-shrink: 0;
65
53
  }
66
54
 
67
55
  .iw-dialog-description {
@@ -80,3 +68,38 @@
80
68
  flex-wrap: wrap;
81
69
  gap: var(--iw-space-2);
82
70
  }
71
+
72
+ /* A sheet on a narrow screen: anchored to the bottom edge at full width,
73
+ rising from below, clear of the home indicator. The browser centres a modal
74
+ dialog with auto margins in a full-screen inset, so the sheet resets both.
75
+ Only the bottom corners are squared, so a theme's radius stays on top. The
76
+ breakpoint is --iw-breakpoint-md, which a media query cannot read. */
77
+ @media (width < 48rem) {
78
+ dialog.iw-dialog.iw-dialog-sheet {
79
+ inset: auto 0 0;
80
+ margin: 0;
81
+ width: 100%;
82
+ min-width: 0;
83
+ max-width: 100%;
84
+ max-height: 85dvh;
85
+ padding-bottom: var(--safe-area-bottom, 0px);
86
+ border-end-start-radius: 0;
87
+ border-end-end-radius: 0;
88
+ }
89
+
90
+ dialog.iw-dialog.iw-dialog-sheet[open] {
91
+ animation: iw-dialog-sheet-in 0.32s cubic-bezier(0.22, 1, 0.36, 1);
92
+ }
93
+ }
94
+
95
+ @keyframes iw-dialog-sheet-in {
96
+ from {
97
+ transform: translateY(100%);
98
+ }
99
+ }
100
+
101
+ @media (prefers-reduced-motion: reduce) {
102
+ dialog.iw-dialog.iw-dialog-sheet[open] {
103
+ animation: none;
104
+ }
105
+ }
@@ -8,6 +8,9 @@ import { classMap } from "@inglorious/web/directives/class-map"
8
8
  import { ref } from "@inglorious/web/directives/ref"
9
9
  import { when } from "@inglorious/web/directives/when"
10
10
 
11
+ import { IconButton } from "../../controls/icon-button/index.js"
12
+ import { Icon } from "../../data-display/icon/index.js"
13
+ import { close } from "../../shared/icons.js"
11
14
  import { syncDialog } from "../../shared/top-layer.js"
12
15
 
13
16
  /**
@@ -29,12 +32,14 @@ export function render(props) {
29
32
  id,
30
33
  isOpen = false,
31
34
  isModal = true,
35
+ variant = "dialog",
32
36
  size = "md",
33
37
  title,
34
38
  description,
35
39
  children,
36
40
  actions,
37
41
  className = "",
42
+ closeLabel = "Close",
38
43
  onClose,
39
44
  onBackdropClick,
40
45
  } = props
@@ -59,6 +64,7 @@ export function render(props) {
59
64
  class=${classMap({
60
65
  "iw-dialog": true,
61
66
  [`iw-dialog-${size}`]: true,
67
+ "iw-dialog-sheet": variant === "sheet",
62
68
  ...extraClasses,
63
69
  })}
64
70
  aria-labelledby=${titleId || nothing}
@@ -94,17 +100,16 @@ export function render(props) {
94
100
  ${title}
95
101
  </div>`,
96
102
  )}
97
- ${when(
98
- onClose,
99
- () =>
100
- html`<button
101
- type="button"
102
- class="iw-dialog-close"
103
- aria-label="Close"
104
- @click=${onClose}
105
- >
106
- ×
107
- </button>`,
103
+ ${when(onClose, () =>
104
+ IconButton.render({
105
+ icon: Icon.render({ icon: close, size: "lg" }),
106
+ ariaLabel: closeLabel,
107
+ variant: "ghost",
108
+ color: "secondary",
109
+ shape: "round",
110
+ className: "iw-dialog-close",
111
+ onClick: onClose,
112
+ }),
108
113
  )}
109
114
  </div>`,
110
115
  )}
@@ -8,7 +8,7 @@ import { classMap } from "@inglorious/web/directives/class-map"
8
8
  import { ref } from "@inglorious/web/directives/ref"
9
9
  import { when } from "@inglorious/web/directives/when"
10
10
 
11
- import { Button } from "../../controls/button/index.js"
11
+ import { IconButton } from "../../controls/icon-button/index.js"
12
12
  import { Icon } from "../../data-display/icon/index.js"
13
13
  import { Flex } from "../../layout/flex/index.js"
14
14
  import { applyElementProps } from "../../shared/apply-element-props.js"
@@ -51,6 +51,9 @@ export function render(props) {
51
51
  title,
52
52
  children,
53
53
  className = "",
54
+ closeLabel = "Close",
55
+ collapseLabel = "Collapse sidebar",
56
+ expandLabel = "Expand sidebar",
54
57
  onClose,
55
58
  onCollapseToggle,
56
59
  ...rest
@@ -103,14 +106,14 @@ export function render(props) {
103
106
  () => html`<div class="iw-drawer-title">${title}</div>`,
104
107
  ),
105
108
  when(onClose, () =>
106
- Button.render({
109
+ IconButton.render({
110
+ icon: Icon.render({ icon: close, size: "lg" }),
111
+ ariaLabel: closeLabel,
107
112
  color: "secondary",
108
113
  variant: "ghost",
109
114
  shape: "square",
110
115
  className: "iw-drawer-close",
111
- "aria-label": "Close",
112
116
  onClick: onClose,
113
- children: Icon.render({ icon: close, size: "lg" }),
114
117
  }),
115
118
  ),
116
119
  ],
@@ -129,14 +132,14 @@ export function render(props) {
129
132
  justify: "end",
130
133
  padding: "sm",
131
134
  className: "iw-drawer-footer",
132
- children: Button.render({
135
+ children: IconButton.render({
136
+ icon: Icon.render({ icon: leftPanelClose, size: "lg" }),
137
+ ariaLabel: isCollapsed ? expandLabel : collapseLabel,
133
138
  color: "secondary",
134
139
  variant: "ghost",
135
140
  shape: "square",
136
141
  className: "iw-drawer-toggler",
137
- "aria-label": isCollapsed ? "Expand sidebar" : "Collapse sidebar",
138
142
  onClick: onCollapseToggle,
139
- children: Icon.render({ icon: leftPanelClose, size: "lg" }),
140
143
  }),
141
144
  })}
142
145
  `,
@@ -8,11 +8,19 @@ export interface DialogProps {
8
8
  isModal?: boolean
9
9
  /** Its width: `"sm"`, `"md"` (default) or `"lg"`. */
10
10
  size?: "sm" | "md" | "lg"
11
+ /**
12
+ * How it presents itself: `"dialog"` (default) centred, or `"sheet"`, which
13
+ * below 48rem is anchored to the bottom edge at full width, rising from
14
+ * below, and from 48rem is the centred dialog. Either can be modal or not.
15
+ */
16
+ variant?: "dialog" | "sheet"
11
17
  title?: TemplateResult | string | number
12
18
  description?: TemplateResult | string | number
13
19
  children?: TemplateResult | string | number
14
20
  actions?: TemplateResult | string | number
15
21
  className?: string
22
+ /** The close button's accessible name, for the app's language. Defaults to `"Close"`. */
23
+ closeLabel?: string
16
24
  onClose?: () => void
17
25
  onBackdropClick?: () => void
18
26
  [key: string]: unknown
@@ -16,6 +16,12 @@ export interface DrawerProps {
16
16
  children?: TemplateResult | string | number
17
17
  hasBackdrop?: boolean
18
18
  className?: string
19
+ /** The close button's accessible name, for the app's language. Defaults to `"Close"`. */
20
+ closeLabel?: string
21
+ /** The collapse toggle's accessible name while expanded. Defaults to `"Collapse sidebar"`. */
22
+ collapseLabel?: string
23
+ /** The collapse toggle's accessible name while collapsed. Defaults to `"Expand sidebar"`. */
24
+ expandLabel?: string
19
25
  onClose?: () => void
20
26
  onCollapseToggle?: () => void
21
27
  [key: string]: unknown