@inglorious/ui 9.8.0 → 9.8.2
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 +8 -8
- package/src/controls/button/style.css +0 -2
- package/src/controls/button/template.js +2 -3
- package/src/controls/checkbox/style.css +0 -1
- package/src/controls/checkbox/template.js +0 -2
- package/src/controls/combobox/style.css +0 -14
- package/src/controls/combobox/template.js +0 -13
- package/src/controls/file-upload/template.js +1 -1
- package/src/controls/image-cropper/helpers.js +0 -2
- package/src/controls/image-cropper/image.js +0 -2
- package/src/controls/image-cropper/style.css +0 -6
- package/src/controls/image-cropper/template.js +3 -5
- package/src/controls/input/style.css +0 -1
- package/src/controls/radio-group/style.css +0 -2
- package/src/controls/rating/style.css +0 -3
- package/src/controls/select/style.css +0 -6
- package/src/controls/slider/style.css +0 -5
- package/src/controls/slider/template.js +0 -1
- package/src/controls/switch/style.css +0 -1
- package/src/controls/switch/template.js +0 -2
- package/src/controls/textarea/style.css +0 -2
- package/src/data-display/before-after/style.css +0 -4
- package/src/data-display/before-after/template.js +3 -3
- package/src/data-display/carousel/carousel.stories.js +0 -2
- package/src/data-display/carousel/handlers.js +0 -2
- package/src/data-display/carousel/style.css +0 -18
- package/src/data-display/carousel/template.js +0 -15
- package/src/data-display/chat/index.js +0 -1
- package/src/data-display/chat/style.css +0 -2
- package/src/data-display/chat/template.js +0 -1
- package/src/data-display/chip/template.js +0 -1
- package/src/data-display/data-grid/base.css +0 -2
- package/src/data-display/data-grid/handlers.js +1 -8
- package/src/data-display/data-grid/helpers.js +0 -5
- package/src/data-display/data-grid/template.js +0 -1
- package/src/data-display/data-grid/theme.css +0 -1
- package/src/data-display/icon/style.css +0 -7
- package/src/data-display/icon/template.js +2 -2
- package/src/data-display/table/style.css +0 -2
- package/src/data-display/tooltip/style.css +0 -3
- package/src/data-display/virtual-list/handlers.js +0 -2
- package/src/data-display/virtual-list/template.js +2 -4
- package/src/examples/dashboard/before-after-data.js +0 -3
- package/src/examples/dashboard/data.js +0 -1
- package/src/examples/dashboard/primitive-content.js +0 -2
- package/src/examples/dashboard/style.css +0 -3
- package/src/feedback/alert/style.css +0 -1
- package/src/feedback/dialog/dialog.stories.js +0 -4
- package/src/feedback/dialog/style.css +0 -10
- package/src/feedback/dialog/template.js +0 -5
- package/src/feedback/progress/style.css +0 -6
- package/src/feedback/progress/template.js +0 -3
- package/src/feedback/snackbar/snackbar.stories.js +0 -1
- package/src/feedback/snackbar/style.css +0 -10
- package/src/layout/flex/style.css +0 -2
- package/src/navigation/bottom-navigation/style.css +0 -7
- package/src/navigation/drawer/style.css +0 -21
- package/src/navigation/drawer/template.js +0 -2
- package/src/navigation/menu/menu.stories.js +0 -2
- package/src/navigation/menu/style.css +0 -4
- package/src/navigation/menu/template.js +0 -3
- package/src/navigation/speed-dial/style.css +0 -4
- package/src/navigation/speed-dial/template.js +0 -5
- package/src/navigation/tabs/template.js +0 -2
- package/src/navigation/view-transitions/index.js +0 -13
- package/src/navigation/view-transitions/style.css +0 -2
- package/src/shared/icons.js +0 -4
- package/src/styles/themes/bootstrap.css +0 -40
- package/src/styles/themes/inglorious.css +0 -21
- package/src/styles/themes/ios-18.css +0 -47
- package/src/styles/themes/ios-26.css +0 -55
- package/src/styles/themes/material-3.css +0 -52
- package/src/styles/themes/material-ui.css +0 -43
- package/src/styles/tokens.css +0 -40
- package/src/surfaces/accordion/style.css +41 -3
- package/src/surfaces/accordion/template.js +0 -4
- package/src/surfaces/app-bar/style.css +0 -3
- package/src/surfaces/card/style.css +0 -3
- package/types/controls/button-group.d.ts +5 -3
- package/types/controls/button.d.ts +3 -1
- package/types/controls/combobox.d.ts +13 -0
- package/types/controls/fab.d.ts +1 -1
- package/types/controls/icon-button.d.ts +7 -21
- package/types/controls/input.d.ts +2 -2
- package/types/data-display/avatar.d.ts +1 -1
- package/types/data-display/badge.d.ts +4 -2
- package/types/data-display/chip.d.ts +5 -3
- package/types/data-display/icon.d.ts +4 -2
- package/types/data-display/tooltip.d.ts +4 -2
- package/types/data-display/typography.d.ts +3 -1
- package/types/feedback/alert.d.ts +3 -1
- package/types/feedback/backdrop.d.ts +4 -2
- package/types/feedback/dialog.d.ts +3 -1
- package/types/index.d.ts +1 -0
- package/types/layout/container.d.ts +3 -1
- package/types/layout/flex.d.ts +9 -11
- package/types/layout/grid.d.ts +4 -2
- package/types/navigation/breadcrumbs.d.ts +1 -1
- package/types/navigation/drawer.d.ts +3 -1
- package/types/navigation/link.d.ts +4 -2
- package/types/shared/renderable.d.ts +12 -0
- package/types/surfaces/app-bar.d.ts +3 -1
- package/types/surfaces/card.d.ts +4 -2
- package/types/surfaces/paper.d.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/ui",
|
|
3
|
-
"version": "9.8.
|
|
3
|
+
"version": "9.8.2",
|
|
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",
|
|
@@ -316,32 +316,32 @@
|
|
|
316
316
|
"peerDependencies": {
|
|
317
317
|
"@inglorious/store": "^12.1.1",
|
|
318
318
|
"@inglorious/utils": "^5.1.0",
|
|
319
|
-
"@inglorious/web": "^6.
|
|
319
|
+
"@inglorious/web": "^6.13.1"
|
|
320
320
|
},
|
|
321
321
|
"devDependencies": {
|
|
322
322
|
"@iconify-icons/fa7-brands": "^2.0.0",
|
|
323
323
|
"@iconify-icons/fa7-regular": "^2.0.0",
|
|
324
324
|
"@iconify-icons/fa7-solid": "^2.0.0",
|
|
325
325
|
"@iconify-icons/material-symbols": "^2.0.6",
|
|
326
|
-
"@inglorious/charts": "12.2.
|
|
326
|
+
"@inglorious/charts": "12.2.6",
|
|
327
327
|
"@inglorious/eslint-config": "1.2.0",
|
|
328
328
|
"@inglorious/store": "12.1.1",
|
|
329
329
|
"@inglorious/vite-plugin-jsx": "2.0.6",
|
|
330
330
|
"@inglorious/vite-plugin-vue": "1.0.7",
|
|
331
|
-
"@inglorious/web": "6.
|
|
332
|
-
"@storybook/addon-docs": "^10.6.
|
|
333
|
-
"@storybook/web-components-vite": "^10.6.
|
|
331
|
+
"@inglorious/web": "6.13.1",
|
|
332
|
+
"@storybook/addon-docs": "^10.6.5",
|
|
333
|
+
"@storybook/web-components-vite": "^10.6.5",
|
|
334
334
|
"happy-dom": "^20.14.0",
|
|
335
335
|
"jsdom": "^30.0.1",
|
|
336
336
|
"prettier": "^3.9.6",
|
|
337
337
|
"remark-gfm": "^4.0.1",
|
|
338
|
-
"storybook": "^10.6.
|
|
338
|
+
"storybook": "^10.6.5",
|
|
339
339
|
"typescript": "~7.0.2",
|
|
340
340
|
"vite": "^8.2.2",
|
|
341
341
|
"vitest": "^5.0.0"
|
|
342
342
|
},
|
|
343
343
|
"engines": {
|
|
344
|
-
"node": ">=
|
|
344
|
+
"node": ">= 26"
|
|
345
345
|
},
|
|
346
346
|
"scripts": {
|
|
347
347
|
"dev": "vite --config app/vite.config.js",
|
|
@@ -60,7 +60,6 @@
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
/* Neutral, unless a theme gives the default color its own tokens */
|
|
64
63
|
&.iw-button-default {
|
|
65
64
|
--iw-button-color: var(--iw-button-default-color, var(--iw-color-text));
|
|
66
65
|
--iw-button-bg: var(--iw-button-default-bg, var(--iw-color-surface));
|
|
@@ -144,7 +143,6 @@
|
|
|
144
143
|
&.iw-button-pressed {
|
|
145
144
|
background-color: var(--iw-button-bg-active);
|
|
146
145
|
color: var(--iw-button-text);
|
|
147
|
-
/* box-shadow: var(--iw-button-shadow-active); */
|
|
148
146
|
}
|
|
149
147
|
|
|
150
148
|
&:disabled,
|
|
@@ -16,7 +16,7 @@ import { applyElementProps } from "../../shared/apply-element-props.js"
|
|
|
16
16
|
* With `onFileSelect`, the button also opens a file picker on click.
|
|
17
17
|
* @example
|
|
18
18
|
* // Stateless usage
|
|
19
|
-
* button.render({ children: 'Click me',
|
|
19
|
+
* button.render({ children: 'Click me', color: 'primary' })
|
|
20
20
|
*
|
|
21
21
|
* // File picker
|
|
22
22
|
* button.render({ children: 'Upload', accept: 'image/*', onFileSelect })
|
|
@@ -89,7 +89,7 @@ export function render(props) {
|
|
|
89
89
|
() =>
|
|
90
90
|
html`<input
|
|
91
91
|
type="file"
|
|
92
|
-
accept=${accept ??
|
|
92
|
+
accept=${accept ?? nothing}
|
|
93
93
|
?multiple=${isMultiple}
|
|
94
94
|
?disabled=${isDisabled}
|
|
95
95
|
hidden
|
|
@@ -102,6 +102,5 @@ export function render(props) {
|
|
|
102
102
|
function handleFileChange(event, onFileSelect) {
|
|
103
103
|
const { files } = event.target
|
|
104
104
|
if (files?.length) onFileSelect(files)
|
|
105
|
-
// Clear the input so picking the same file again still fires `change`
|
|
106
105
|
event.target.value = ""
|
|
107
106
|
}
|
|
@@ -34,8 +34,6 @@ export function render(props) {
|
|
|
34
34
|
...rest
|
|
35
35
|
} = props
|
|
36
36
|
|
|
37
|
-
// The label wraps the input, which is what ties the two together: it needs no
|
|
38
|
-
// `for`, and an empty one would point at nothing and break the click-through.
|
|
39
37
|
const inputId = id || name
|
|
40
38
|
|
|
41
39
|
const classes = {
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
flex-direction: column;
|
|
6
6
|
gap: var(--iw-space-1);
|
|
7
7
|
font-family: var(--iw-font-family-sans);
|
|
8
|
-
/* Each combobox anchors its own dropdown to its own frame. */
|
|
9
8
|
anchor-scope: --iw-combobox;
|
|
10
9
|
|
|
11
10
|
&.iw-combobox-full-width {
|
|
@@ -42,7 +41,6 @@
|
|
|
42
41
|
anchor-name: --iw-combobox;
|
|
43
42
|
display: flex;
|
|
44
43
|
align-items: center;
|
|
45
|
-
/* The gap spaces the text from the arrow, as it does for Input's icons. */
|
|
46
44
|
gap: var(--iw-space-2);
|
|
47
45
|
padding: 0 var(--iw-space-3);
|
|
48
46
|
border: 1px solid var(--iw-input-border);
|
|
@@ -82,7 +80,6 @@
|
|
|
82
80
|
box-shadow: var(--iw-input-shadow-focus);
|
|
83
81
|
}
|
|
84
82
|
|
|
85
|
-
/* The control is focusable so it can be opened from the keyboard. */
|
|
86
83
|
&:focus-visible {
|
|
87
84
|
outline: none;
|
|
88
85
|
border-color: var(--iw-input-border-focus);
|
|
@@ -110,9 +107,6 @@
|
|
|
110
107
|
}
|
|
111
108
|
}
|
|
112
109
|
|
|
113
|
-
/* The text element, whatever it is showing. Same role as
|
|
114
|
-
.iw-input-control in Input, so the shared rules live here and the
|
|
115
|
-
variant classes only add what is specific to them. */
|
|
116
110
|
.iw-combobox-control {
|
|
117
111
|
flex: 1;
|
|
118
112
|
min-width: 0;
|
|
@@ -168,11 +162,6 @@
|
|
|
168
162
|
justify-content: center;
|
|
169
163
|
font-size: var(--iw-font-size-sm);
|
|
170
164
|
line-height: 1;
|
|
171
|
-
/* A flex child, like Input's icons and the Select arrow. Absolutely
|
|
172
|
-
positioning it anchored to .iw-combobox rather than the frame, so
|
|
173
|
-
the glyph drifted whenever a label sat above the control, and it
|
|
174
|
-
needed a reserved padding strip to stop text running under it. The
|
|
175
|
-
row's gap does both. */
|
|
176
165
|
flex: none;
|
|
177
166
|
pointer-events: none;
|
|
178
167
|
color: var(--iw-color-text-muted);
|
|
@@ -184,9 +173,6 @@
|
|
|
184
173
|
}
|
|
185
174
|
}
|
|
186
175
|
|
|
187
|
-
/* A popover in the top layer, placed under the frame and as wide as it,
|
|
188
|
-
flipping above when there is no room below. The browser's popover box
|
|
189
|
-
is centred in the viewport, so its insets, margin and colours are reset. */
|
|
190
176
|
.iw-combobox-dropdown {
|
|
191
177
|
position-anchor: --iw-combobox;
|
|
192
178
|
position-area: block-end span-inline-end;
|
|
@@ -51,11 +51,6 @@ export const Combobox = {
|
|
|
51
51
|
isFullWidth = false,
|
|
52
52
|
isOpen = false,
|
|
53
53
|
} = props
|
|
54
|
-
// The control is a div, not a labellable element, so a `for` binding is not
|
|
55
|
-
// available. It is referenced by id from the control's aria-labelledby
|
|
56
|
-
// instead, which is what a screen reader actually reads here.
|
|
57
|
-
// Only minted when there is something to point at. A reference to an id
|
|
58
|
-
// that is not in the DOM is worse than no reference at all.
|
|
59
54
|
const labelId = label && id ? `${id}-label` : ""
|
|
60
55
|
const messageId = (error || hint) && id ? `${id}-message` : ""
|
|
61
56
|
|
|
@@ -535,25 +530,17 @@ function trackOutsideClick(props, el) {
|
|
|
535
530
|
|
|
536
531
|
const existing = outsideListeners.get(el)
|
|
537
532
|
if (existing) {
|
|
538
|
-
// A re-render of the same dropdown: keep the one listener, but point it at
|
|
539
|
-
// the current props so onClose is not captured from the first render.
|
|
540
533
|
existing.props = props
|
|
541
534
|
return
|
|
542
535
|
}
|
|
543
536
|
|
|
544
537
|
const entry = { props }
|
|
545
538
|
entry.listener = (event) => {
|
|
546
|
-
// The dropdown left the DOM: this press is the last one to hear from it.
|
|
547
|
-
// Retiring here is what keeps a torn-down dropdown from closing whatever
|
|
548
|
-
// opens next.
|
|
549
539
|
if (!el.isConnected) {
|
|
550
540
|
document.removeEventListener("pointerdown", entry.listener)
|
|
551
541
|
return
|
|
552
542
|
}
|
|
553
543
|
|
|
554
|
-
// The root, not the dropdown: the control is a sibling of the dropdown, and a
|
|
555
|
-
// press on it has to count as inside. Resolved per event rather than at mount
|
|
556
|
-
// because the dropdown is not connected yet when the ref first runs.
|
|
557
544
|
const root = el.closest(".iw-combobox") ?? el
|
|
558
545
|
if (!event.composedPath().includes(root)) entry.props.onClose?.()
|
|
559
546
|
}
|
|
@@ -79,7 +79,7 @@ export function render(props) {
|
|
|
79
79
|
aria-describedby=${messageId || nothing}
|
|
80
80
|
aria-invalid=${error ? "true" : "false"}
|
|
81
81
|
type="file"
|
|
82
|
-
accept=${accept ??
|
|
82
|
+
accept=${accept ?? nothing}
|
|
83
83
|
?multiple=${isMultiple}
|
|
84
84
|
?disabled=${isDisabled}
|
|
85
85
|
?required=${isRequired}
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import { clamp } from "@inglorious/utils/math/number.js"
|
|
7
7
|
|
|
8
|
-
// Every rect is in fractions of the image (0-1 on both axes), so it survives
|
|
9
|
-
// any display size and maps onto source pixels only when the image is cropped.
|
|
10
8
|
export const DEFAULT_MIN_SIZE = 0.1
|
|
11
9
|
export const DEFAULT_STEP = 0.02
|
|
12
10
|
export const FULL_RECT = { x: 0, y: 0, w: 1, h: 1 }
|
|
@@ -50,7 +50,6 @@ export async function cropImage(file, rect, options = {}) {
|
|
|
50
50
|
const context = canvas.getContext("2d")
|
|
51
51
|
if (!context) throw new Error("Canvas is unavailable")
|
|
52
52
|
|
|
53
|
-
// Formats without alpha would turn transparent pixels black.
|
|
54
53
|
if (OPAQUE_TYPES.includes(type) && background) {
|
|
55
54
|
context.fillStyle = background
|
|
56
55
|
context.fillRect(0, 0, output.width, output.height)
|
|
@@ -86,7 +85,6 @@ export async function decodeImage(file) {
|
|
|
86
85
|
try {
|
|
87
86
|
return await createImageBitmap(file, { imageOrientation: "from-image" })
|
|
88
87
|
} catch {
|
|
89
|
-
// Some engines reject the options object; try the plain decoder.
|
|
90
88
|
try {
|
|
91
89
|
return await createImageBitmap(file)
|
|
92
90
|
} catch {
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
.iw-image-cropper {
|
|
2
|
-
/* Set from the image's natural size once it loads. */
|
|
3
2
|
--iw-image-cropper-aspect: 16 / 9;
|
|
4
|
-
/* Caps the stage height; the width follows the image's proportions. */
|
|
5
3
|
--iw-image-cropper-max-height: 70vh;
|
|
6
4
|
|
|
7
|
-
/* Neutral in every theme on purpose, like the before/after divider: the
|
|
8
|
-
frame must read on any photo without competing with it. */
|
|
9
5
|
--iw-image-cropper-bg: var(--iw-color-gray-900);
|
|
10
6
|
--iw-image-cropper-overlay: rgb(0 0 0 / 55%);
|
|
11
7
|
--iw-image-cropper-frame-width: 2px;
|
|
@@ -57,8 +53,6 @@
|
|
|
57
53
|
-webkit-user-drag: none;
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
/* The events land on the stage either way; the frame and handles only take
|
|
61
|
-
pointer events so the cursor shows what a drag there would do. */
|
|
62
56
|
.iw-image-cropper-frame {
|
|
63
57
|
position: absolute;
|
|
64
58
|
box-sizing: border-box;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { html } from "@inglorious/web"
|
|
7
|
+
import { html, nothing } from "@inglorious/web"
|
|
8
8
|
import { classMap } from "@inglorious/web/directives/class-map"
|
|
9
9
|
import { when } from "@inglorious/web/directives/when"
|
|
10
10
|
|
|
@@ -34,8 +34,6 @@ const ARROW_MOVES = {
|
|
|
34
34
|
ArrowDown: [0, 1],
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
// Pointers and the drag in progress belong to one element and never reach the
|
|
38
|
-
// store: they change on every move and are meaningless once the gesture ends.
|
|
39
37
|
/** @type {WeakMap<Element, { pointers: Map<number, { x: number, y: number }>, drag: object | null, pinch: object | null }>} */
|
|
40
38
|
const gestures = new WeakMap()
|
|
41
39
|
|
|
@@ -68,7 +66,7 @@ export const ImageCropper = {
|
|
|
68
66
|
}
|
|
69
67
|
const style = isLoaded
|
|
70
68
|
? `--iw-image-cropper-aspect: ${props.naturalWidth / props.naturalHeight};`
|
|
71
|
-
:
|
|
69
|
+
: nothing
|
|
72
70
|
|
|
73
71
|
return html`<div
|
|
74
72
|
class=${classMap(classes)}
|
|
@@ -131,7 +129,7 @@ export const ImageCropper = {
|
|
|
131
129
|
renderImage(props) {
|
|
132
130
|
return html`<img
|
|
133
131
|
class="iw-image-cropper-image"
|
|
134
|
-
src=${props.src ??
|
|
132
|
+
src=${props.src ?? nothing}
|
|
135
133
|
alt=${props.alt ?? ""}
|
|
136
134
|
draggable="false"
|
|
137
135
|
@load=${handleLoad(props)}
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
cursor: pointer;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/* With a description, the label and it stack beside the circle. */
|
|
23
22
|
.iw-radio-text {
|
|
24
23
|
display: flex;
|
|
25
24
|
flex-direction: column;
|
|
@@ -71,7 +70,6 @@
|
|
|
71
70
|
--iw-radio-accent: var(--iw-color-info);
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
/* After the directions, which it overrides with a grid. */
|
|
75
73
|
&.iw-radio-group-card {
|
|
76
74
|
display: grid;
|
|
77
75
|
grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
|
|
@@ -37,8 +37,6 @@
|
|
|
37
37
|
.iw-select-frame {
|
|
38
38
|
display: flex;
|
|
39
39
|
align-items: center;
|
|
40
|
-
/* The gap spaces the text from the arrow, the same way it spaces an
|
|
41
|
-
Input's text from its icons. */
|
|
42
40
|
gap: var(--iw-space-2);
|
|
43
41
|
padding: 0 var(--iw-space-3);
|
|
44
42
|
border: 1px solid var(--iw-input-border);
|
|
@@ -77,7 +75,6 @@
|
|
|
77
75
|
}
|
|
78
76
|
}
|
|
79
77
|
|
|
80
|
-
/* The control is focusable so it can be opened from the keyboard. */
|
|
81
78
|
&:focus-visible {
|
|
82
79
|
outline: none;
|
|
83
80
|
border-color: var(--iw-input-border-focus);
|
|
@@ -149,9 +146,6 @@
|
|
|
149
146
|
justify-content: center;
|
|
150
147
|
font-size: var(--iw-font-size-sm);
|
|
151
148
|
line-height: 1;
|
|
152
|
-
/* A flex child, like Input's icons. Absolutely positioning it needed a
|
|
153
|
-
positioning context on the frame and a reserved padding strip so the
|
|
154
|
-
text would not run underneath it; the row's gap does both. */
|
|
155
149
|
flex: none;
|
|
156
150
|
/* Decorative. A popup only opens from a trusted gesture on the select
|
|
157
151
|
itself, so the arrow cannot open it; leaving clicks to pass through
|
|
@@ -23,8 +23,6 @@
|
|
|
23
23
|
accent-color: var(--iw-slider-accent);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/* A tick per step, under the track: their ends sit under the thumb's ends
|
|
27
|
-
of travel, half a thumb in from the track's. */
|
|
28
26
|
.iw-slider-marks {
|
|
29
27
|
display: flex;
|
|
30
28
|
justify-content: space-between;
|
|
@@ -37,9 +35,6 @@
|
|
|
37
35
|
}
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
/* Two thumbs on one track. The inputs lie on top of each other with their
|
|
41
|
-
own tracks hidden, and only their thumbs take presses, so either can be
|
|
42
|
-
dragged; the track below is drawn here, filled between the two. */
|
|
43
38
|
&.iw-slider-range {
|
|
44
39
|
--iw-slider-thumb: 1rem;
|
|
45
40
|
|
|
@@ -132,7 +132,6 @@ function renderRange({
|
|
|
132
132
|
const span = max - min || DEFAULT_STEP
|
|
133
133
|
const fill = `--iw-slider-low: ${((low - min) / span) * PERCENT}%; --iw-slider-high: ${((high - min) / span) * PERCENT}%`
|
|
134
134
|
const endName = (end) => (name ? `${name}-${end}` : nothing)
|
|
135
|
-
// Each thumb is named for its end of the range, after the slider's label.
|
|
136
135
|
const endLabel = (end) =>
|
|
137
136
|
typeof label === "string" && label ? `${label} ${end}` : end
|
|
138
137
|
|
|
@@ -32,8 +32,6 @@ export function render(props) {
|
|
|
32
32
|
...rest
|
|
33
33
|
} = props
|
|
34
34
|
|
|
35
|
-
// The label wraps the input, which is what ties the two together: it needs no
|
|
36
|
-
// `for`, and an empty one would point at nothing and break the click-through.
|
|
37
35
|
const inputId = id || name
|
|
38
36
|
|
|
39
37
|
const classes = {
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
gap: var(--iw-space-1);
|
|
5
5
|
font-family: var(--iw-font-family-sans);
|
|
6
6
|
|
|
7
|
-
/* The field takes the width it is given, and the box inside fills it. */
|
|
8
7
|
&.iw-textarea-full-width {
|
|
9
8
|
width: 100%;
|
|
10
9
|
|
|
@@ -24,7 +23,6 @@
|
|
|
24
23
|
margin-inline-start: var(--iw-space-1);
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
/* Its height is its rows', or its content's with isAutoSize. */
|
|
28
26
|
.iw-textarea {
|
|
29
27
|
width: min(100%, 100%);
|
|
30
28
|
resize: none;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
.iw-before-after {
|
|
2
2
|
--iw-before-after-position: 50%;
|
|
3
3
|
|
|
4
|
-
/* Neutral in every theme on purpose: the divider should separate the two
|
|
5
|
-
pictures without competing with them for attention. */
|
|
6
4
|
--iw-before-after-divider-width: 2px;
|
|
7
5
|
--iw-before-after-divider-color: rgb(255 255 255 / 90%);
|
|
8
6
|
--iw-before-after-handle-size: 40px;
|
|
@@ -31,7 +29,6 @@
|
|
|
31
29
|
width: 100%;
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
/* Greyed out and dimmed, so it reads as "you cannot move this". */
|
|
35
32
|
&.iw-before-after-disabled {
|
|
36
33
|
--iw-before-after-divider-color: rgb(156 163 175 / 60%);
|
|
37
34
|
--iw-before-after-handle-bg: rgb(156 163 175 / 60%);
|
|
@@ -104,7 +101,6 @@
|
|
|
104
101
|
box-shadow: var(--iw-shadow-md);
|
|
105
102
|
}
|
|
106
103
|
|
|
107
|
-
/* Two chevron arrows drawn with borders, no icon font needed. */
|
|
108
104
|
.iw-before-after-handle::before,
|
|
109
105
|
.iw-before-after-handle::after {
|
|
110
106
|
content: "";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { clamp } from "@inglorious/utils/math/number.js"
|
|
7
|
-
import { html } from "@inglorious/web"
|
|
7
|
+
import { html, nothing } from "@inglorious/web"
|
|
8
8
|
import { classMap } from "@inglorious/web/directives/class-map"
|
|
9
9
|
|
|
10
10
|
import {
|
|
@@ -79,7 +79,7 @@ export const BeforeAfter = {
|
|
|
79
79
|
return html`<div class="iw-before-after-item iw-before-after-before">
|
|
80
80
|
<img
|
|
81
81
|
class="iw-before-after-image"
|
|
82
|
-
src=${props.before?.src ??
|
|
82
|
+
src=${props.before?.src ?? nothing}
|
|
83
83
|
alt=${props.before?.alt ?? ""}
|
|
84
84
|
draggable="false"
|
|
85
85
|
/>
|
|
@@ -95,7 +95,7 @@ export const BeforeAfter = {
|
|
|
95
95
|
return html`<div class="iw-before-after-item iw-before-after-after">
|
|
96
96
|
<img
|
|
97
97
|
class="iw-before-after-image"
|
|
98
|
-
src=${props.after?.src ??
|
|
98
|
+
src=${props.after?.src ?? nothing}
|
|
99
99
|
alt=${props.after?.alt ?? ""}
|
|
100
100
|
draggable="false"
|
|
101
101
|
/>
|
|
@@ -149,8 +149,6 @@ export const ManyPerView = {}
|
|
|
149
149
|
ManyPerView.args = {
|
|
150
150
|
...Default.args,
|
|
151
151
|
id: "carousel-many",
|
|
152
|
-
// The infinite treadmill keeps one slide at its centre, so it is not a good
|
|
153
|
-
// fit when several slides are intentionally visible at once.
|
|
154
152
|
isInfinite: false,
|
|
155
153
|
}
|
|
156
154
|
ManyPerView.decorators = [
|
|
@@ -40,8 +40,6 @@ export function create(entity) {
|
|
|
40
40
|
entity.hasIndicators ??= true
|
|
41
41
|
entity.isFullWidth ??= false
|
|
42
42
|
|
|
43
|
-
// An infinite carousel keeps the current item in the middle slot, so the
|
|
44
|
-
// starting item is placed there by rotating the strip up front.
|
|
45
43
|
if (entity.isInfinite && entity.items.length > ENOUGH_TO_WRAP) {
|
|
46
44
|
const home = Math.floor(entity.items.length / 2)
|
|
47
45
|
entity.rotation = normalizeRotation(entity.page - home, entity.items.length)
|
|
@@ -17,14 +17,11 @@
|
|
|
17
17
|
width: 100%;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/* A column stage otherwise shrink-wraps to its arrows instead of the
|
|
21
|
-
available inline size. */
|
|
22
20
|
&.iw-carousel-vertical {
|
|
23
21
|
display: block;
|
|
24
22
|
width: 100%;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
|
-
/* Snapping, swiping, momentum and RTL all come from the browser. */
|
|
28
25
|
.iw-carousel-viewport {
|
|
29
26
|
position: relative;
|
|
30
27
|
display: flex;
|
|
@@ -45,19 +42,11 @@
|
|
|
45
42
|
outline-offset: 2px;
|
|
46
43
|
}
|
|
47
44
|
|
|
48
|
-
/* One item per view by default, so every item can reach the start of the
|
|
49
|
-
viewport and each one is exactly one page. Override
|
|
50
|
-
--iw-carousel-item-size to show several at once. */
|
|
51
45
|
.iw-carousel-item {
|
|
52
46
|
flex: 0 0 var(--iw-carousel-item-size, 100%);
|
|
53
47
|
scroll-snap-align: start;
|
|
54
48
|
}
|
|
55
49
|
|
|
56
|
-
/* Smooth scrolling would animate every drag step, so it is turned off while
|
|
57
|
-
dragging. Snapping stays on: the browser then lands on the nearest slide by
|
|
58
|
-
itself, and re-enabling it instead would snap back to the item it
|
|
59
|
-
remembered. Doubled up on the class so this outranks the reduced-motion
|
|
60
|
-
rule at the end of the file. */
|
|
61
50
|
.iw-carousel-viewport.iw-carousel-viewport-dragging {
|
|
62
51
|
scroll-behavior: auto;
|
|
63
52
|
scroll-snap-type: none;
|
|
@@ -77,7 +66,6 @@
|
|
|
77
66
|
overscroll-behavior-y: contain;
|
|
78
67
|
}
|
|
79
68
|
|
|
80
|
-
/* Gap tokens mirror Flex. */
|
|
81
69
|
&.iw-carousel-gap-sm .iw-carousel-viewport {
|
|
82
70
|
gap: var(--iw-space-2);
|
|
83
71
|
}
|
|
@@ -94,7 +82,6 @@
|
|
|
94
82
|
gap: var(--iw-space-8);
|
|
95
83
|
}
|
|
96
84
|
|
|
97
|
-
/* Off-axis alignment. */
|
|
98
85
|
&.iw-carousel-align-start .iw-carousel-viewport {
|
|
99
86
|
align-items: flex-start;
|
|
100
87
|
}
|
|
@@ -107,7 +94,6 @@
|
|
|
107
94
|
align-items: flex-end;
|
|
108
95
|
}
|
|
109
96
|
|
|
110
|
-
/* The stage holds the viewport and its arrows, so centring ignores the dots. */
|
|
111
97
|
.iw-carousel-stage {
|
|
112
98
|
position: relative;
|
|
113
99
|
display: flex;
|
|
@@ -125,7 +111,6 @@
|
|
|
125
111
|
min-width: 0;
|
|
126
112
|
}
|
|
127
113
|
|
|
128
|
-
/* Inside: float the arrows over the slides. */
|
|
129
114
|
&.iw-carousel-arrows-inside .iw-carousel-stage {
|
|
130
115
|
gap: 0;
|
|
131
116
|
}
|
|
@@ -160,13 +145,11 @@
|
|
|
160
145
|
bottom: var(--iw-carousel-arrow-inset);
|
|
161
146
|
}
|
|
162
147
|
|
|
163
|
-
/* Outside: the arrows sit beside the scrolling area, flex handles centring. */
|
|
164
148
|
&.iw-carousel-arrows-outside .iw-carousel-arrow {
|
|
165
149
|
position: static;
|
|
166
150
|
flex: 0 0 auto;
|
|
167
151
|
}
|
|
168
152
|
|
|
169
|
-
/* Chevrons drawn with borders, no icon font needed. */
|
|
170
153
|
.iw-carousel-chevron {
|
|
171
154
|
display: block;
|
|
172
155
|
width: 0;
|
|
@@ -225,7 +208,6 @@
|
|
|
225
208
|
}
|
|
226
209
|
}
|
|
227
210
|
|
|
228
|
-
/* Respect reduced motion: only animate paging when the user allows it. */
|
|
229
211
|
@media (prefers-reduced-motion: no-preference) {
|
|
230
212
|
.iw-carousel .iw-carousel-viewport {
|
|
231
213
|
scroll-behavior: smooth;
|
|
@@ -69,8 +69,6 @@ export const Carousel = {
|
|
|
69
69
|
"iw-carousel-full-width": isFullWidth,
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
// The arrows live in the stage, next to the viewport, so that centring them
|
|
73
|
-
// ignores the indicators underneath.
|
|
74
72
|
return html`<div class=${classMap(classes)}>
|
|
75
73
|
<div class="iw-carousel-stage">
|
|
76
74
|
${when(hasArrows, () => this.renderArrow?.(props, PREVIOUS))}
|
|
@@ -152,13 +150,10 @@ export const Carousel = {
|
|
|
152
150
|
} = props
|
|
153
151
|
const isPrevious = direction === PREVIOUS
|
|
154
152
|
|
|
155
|
-
// Infinite carousels wrap, so their arrows never run out.
|
|
156
153
|
const isDisabled =
|
|
157
154
|
!isInfinite &&
|
|
158
155
|
(isPrevious ? page <= FIRST_PAGE : page >= getLastPage(items))
|
|
159
156
|
|
|
160
|
-
// The click is caught on the wrapper: Button's onClick carries no event, and
|
|
161
|
-
// a disabled button emits no click at all, so nothing can leak through.
|
|
162
157
|
return html`<span
|
|
163
158
|
class=${classMap({
|
|
164
159
|
"iw-carousel-arrow": true,
|
|
@@ -238,7 +233,6 @@ export const Carousel = {
|
|
|
238
233
|
*/
|
|
239
234
|
function handleScroll(props) {
|
|
240
235
|
return (event) => {
|
|
241
|
-
// Infinite carousels re-centre on settle instead of tracking every scroll.
|
|
242
236
|
if (props.isInfinite) return
|
|
243
237
|
|
|
244
238
|
const page = pageFromScroll(event.currentTarget, props.axis)
|
|
@@ -257,7 +251,6 @@ function handleScrollEnd(props) {
|
|
|
257
251
|
return (event) => {
|
|
258
252
|
const viewport = event.currentTarget
|
|
259
253
|
|
|
260
|
-
// Mid-drag pauses fire scrollend too; leave the drag alone until it ends.
|
|
261
254
|
if (drags.has(viewport)) return
|
|
262
255
|
|
|
263
256
|
viewport.classList.remove("iw-carousel-viewport-dragging")
|
|
@@ -341,7 +334,6 @@ function handlePointerDown(props) {
|
|
|
341
334
|
scroll: isVertical ? viewport.scrollTop : viewport.scrollLeft,
|
|
342
335
|
isVertical,
|
|
343
336
|
})
|
|
344
|
-
// Snapping would fight every move; it is restored on pointerup.
|
|
345
337
|
viewport.classList.add("iw-carousel-viewport-dragging")
|
|
346
338
|
viewport.setPointerCapture?.(event.pointerId)
|
|
347
339
|
}
|
|
@@ -379,8 +371,6 @@ function handlePointerUp(props) {
|
|
|
379
371
|
drags.delete(viewport)
|
|
380
372
|
viewport.releasePointerCapture?.(event.pointerId)
|
|
381
373
|
|
|
382
|
-
// Land on the nearest slide; handleScrollEnd restores snapping and, when
|
|
383
|
-
// infinite, re-centres from there.
|
|
384
374
|
goToPage(viewport, pageFromScroll(viewport, props.axis), props, "smooth")
|
|
385
375
|
}
|
|
386
376
|
}
|
|
@@ -415,9 +405,6 @@ function pageBy(props, step, from) {
|
|
|
415
405
|
return
|
|
416
406
|
}
|
|
417
407
|
|
|
418
|
-
// Several items can be visible at once, so their later offsets may extend
|
|
419
|
-
// past the physical scroll range. At the end, choosing the first such offset
|
|
420
|
-
// would make a right arrow jump backwards after the browser clamps it.
|
|
421
408
|
const limit = maxScroll(viewport, isVertical)
|
|
422
409
|
if (step > 0 && limit > SCROLL_EPSILON && scroll >= limit - SCROLL_EPSILON) {
|
|
423
410
|
return
|
|
@@ -503,8 +490,6 @@ function goToPage(viewport, page, props, behavior = "auto") {
|
|
|
503
490
|
const item = viewport.children[index]
|
|
504
491
|
if (!item) return
|
|
505
492
|
|
|
506
|
-
// Guarded like setPointerCapture: real browsers always have scrollTo, but
|
|
507
|
-
// non-layout environments such as jsdom do not.
|
|
508
493
|
viewport.scrollTo?.(
|
|
509
494
|
(props.axis ?? DEFAULT_AXIS) === VERTICAL_AXIS
|
|
510
495
|
? { top: item.offsetTop, behavior }
|
|
@@ -10,7 +10,6 @@ export const Chat = {
|
|
|
10
10
|
...entity,
|
|
11
11
|
onDraftChange: (draft) => api.notify(`#${id}:draftChange`, draft),
|
|
12
12
|
onSend: (text) => api.notify(`#${id}:messageSend`, text),
|
|
13
|
-
// A message naming an entity, such as a generated answer, shows it.
|
|
14
13
|
renderEntity: (entityId) => api.render(entityId),
|
|
15
14
|
}
|
|
16
15
|
|