@inglorious/ui 9.9.0 → 9.10.1
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 +29 -3
- package/src/controls/checkbox/template.js +3 -1
- package/src/controls/chip-picker/chip-picker.stories.js +22 -0
- package/src/controls/chip-picker/index.js +3 -0
- package/src/controls/chip-picker/style.css +26 -0
- package/src/controls/chip-picker/template.js +53 -0
- package/src/controls/combobox/handlers.js +83 -20
- package/src/controls/combobox/template.js +3 -1
- package/src/controls/controls.css +3 -2
- package/src/controls/radio-group/template.js +3 -1
- package/src/controls/rating/template.js +4 -1
- package/src/controls/select/template.js +6 -12
- package/src/controls/switch/template.js +3 -1
- package/src/data-display/badge/style.css +1 -0
- package/src/data-display/data-display.css +1 -0
- package/src/data-display/lightbox/index.js +3 -0
- package/src/data-display/lightbox/lightbox.stories.js +25 -0
- package/src/data-display/lightbox/style.css +97 -0
- package/src/data-display/lightbox/template.js +153 -0
- package/src/feedback/confirm-dialog/confirm-dialog.stories.js +33 -0
- package/src/feedback/confirm-dialog/index.js +3 -0
- package/src/feedback/confirm-dialog/style.css +16 -0
- package/src/feedback/confirm-dialog/template.js +70 -0
- package/src/feedback/crop-dialog/index.js +3 -0
- package/src/feedback/crop-dialog/style.css +43 -0
- package/src/feedback/crop-dialog/template.js +82 -0
- package/src/feedback/dialog/style.css +2 -1
- package/src/feedback/dialog/template.js +5 -2
- package/src/feedback/feedback.css +3 -0
- package/src/feedback/load-status/index.js +3 -0
- package/src/feedback/load-status/load-status.stories.js +16 -0
- package/src/feedback/load-status/style.css +10 -0
- package/src/feedback/load-status/template.js +43 -0
- package/src/index.js +5 -0
- package/src/shared/icons.js +12 -0
- package/src/shared/live-boolean.js +17 -0
- package/types/controls/chip-picker.d.ts +23 -0
- package/types/controls/combobox.d.ts +20 -2
- package/types/data-display/lightbox.d.ts +35 -0
- package/types/feedback/confirm-dialog.d.ts +34 -0
- package/types/feedback/crop-dialog.d.ts +35 -0
- package/types/feedback/load-status.d.ts +22 -0
- package/types/index.d.ts +17 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/ui",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.10.1",
|
|
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",
|
|
@@ -211,6 +211,31 @@
|
|
|
211
211
|
"import": "./src/feedback/backdrop/index.js"
|
|
212
212
|
},
|
|
213
213
|
"./backdrop.css": "./src/feedback/backdrop/style.css",
|
|
214
|
+
"./chip-picker": {
|
|
215
|
+
"types": "./types/controls/chip-picker.d.ts",
|
|
216
|
+
"import": "./src/controls/chip-picker/index.js"
|
|
217
|
+
},
|
|
218
|
+
"./chip-picker.css": "./src/controls/chip-picker/style.css",
|
|
219
|
+
"./confirm-dialog": {
|
|
220
|
+
"types": "./types/feedback/confirm-dialog.d.ts",
|
|
221
|
+
"import": "./src/feedback/confirm-dialog/index.js"
|
|
222
|
+
},
|
|
223
|
+
"./confirm-dialog.css": "./src/feedback/confirm-dialog/style.css",
|
|
224
|
+
"./crop-dialog": {
|
|
225
|
+
"types": "./types/feedback/crop-dialog.d.ts",
|
|
226
|
+
"import": "./src/feedback/crop-dialog/index.js"
|
|
227
|
+
},
|
|
228
|
+
"./crop-dialog.css": "./src/feedback/crop-dialog/style.css",
|
|
229
|
+
"./lightbox": {
|
|
230
|
+
"types": "./types/data-display/lightbox.d.ts",
|
|
231
|
+
"import": "./src/data-display/lightbox/index.js"
|
|
232
|
+
},
|
|
233
|
+
"./lightbox.css": "./src/data-display/lightbox/style.css",
|
|
234
|
+
"./load-status": {
|
|
235
|
+
"types": "./types/feedback/load-status.d.ts",
|
|
236
|
+
"import": "./src/feedback/load-status/index.js"
|
|
237
|
+
},
|
|
238
|
+
"./load-status.css": "./src/feedback/load-status/style.css",
|
|
214
239
|
"./dialog": {
|
|
215
240
|
"types": "./types/feedback/dialog.d.ts",
|
|
216
241
|
"import": "./src/feedback/dialog/index.js"
|
|
@@ -316,7 +341,7 @@
|
|
|
316
341
|
"peerDependencies": {
|
|
317
342
|
"@inglorious/store": "^12.1.1",
|
|
318
343
|
"@inglorious/utils": "^5.1.0",
|
|
319
|
-
"@inglorious/web": "^6.
|
|
344
|
+
"@inglorious/web": "^6.14.0"
|
|
320
345
|
},
|
|
321
346
|
"devDependencies": {
|
|
322
347
|
"@iconify-icons/fa7-brands": "^2.0.0",
|
|
@@ -328,7 +353,8 @@
|
|
|
328
353
|
"@inglorious/store": "12.1.1",
|
|
329
354
|
"@inglorious/vite-plugin-jsx": "2.0.6",
|
|
330
355
|
"@inglorious/vite-plugin-vue": "1.0.7",
|
|
331
|
-
"@inglorious/web": "6.
|
|
356
|
+
"@inglorious/web": "6.14.0",
|
|
357
|
+
"@lit-labs/ssr": "^4.1.0",
|
|
332
358
|
"@storybook/addon-docs": "^10.6.5",
|
|
333
359
|
"@storybook/web-components-vite": "^10.6.5",
|
|
334
360
|
"happy-dom": "^20.14.0",
|
|
@@ -9,6 +9,7 @@ import { ref } from "@inglorious/web/directives/ref"
|
|
|
9
9
|
import { when } from "@inglorious/web/directives/when"
|
|
10
10
|
|
|
11
11
|
import { applyElementProps } from "../../shared/apply-element-props.js"
|
|
12
|
+
import { liveBoolean } from "../../shared/live-boolean.js"
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Renders a labeled checkbox for boolean input.
|
|
@@ -51,7 +52,8 @@ export function render(props) {
|
|
|
51
52
|
class="iw-checkbox-input"
|
|
52
53
|
type="checkbox"
|
|
53
54
|
name=${name}
|
|
54
|
-
|
|
55
|
+
?checked=${isChecked}
|
|
56
|
+
${liveBoolean("checked", isChecked)}
|
|
55
57
|
?disabled=${isDisabled}
|
|
56
58
|
?required=${isRequired}
|
|
57
59
|
@change=${(event) => onChange?.(event.target.checked)}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createRender } from "../../stories/notify-story.js"
|
|
2
|
+
import { ChipPicker } from "."
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "Controls/ChipPicker",
|
|
6
|
+
tags: ["autodocs"],
|
|
7
|
+
render: createRender(ChipPicker),
|
|
8
|
+
argTypes: { onToggle: { action: "onToggle" } },
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Default = {
|
|
12
|
+
args: {
|
|
13
|
+
label: "What do you do?",
|
|
14
|
+
hint: "Pick as many as you like.",
|
|
15
|
+
options: [
|
|
16
|
+
{ value: "photographer", label: "Photographer" },
|
|
17
|
+
{ value: "videographer", label: "Videographer" },
|
|
18
|
+
{ value: "dj", label: "DJ" },
|
|
19
|
+
],
|
|
20
|
+
value: ["videographer"],
|
|
21
|
+
},
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.iw-chip-picker {
|
|
2
|
+
min-width: 0;
|
|
3
|
+
margin: 0;
|
|
4
|
+
padding: 0;
|
|
5
|
+
border: none;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.iw-chip-picker-legend {
|
|
9
|
+
padding: 0;
|
|
10
|
+
margin-bottom: var(--iw-space-2);
|
|
11
|
+
font-size: var(--iw-font-size-sm);
|
|
12
|
+
font-weight: var(--iw-font-weight-medium);
|
|
13
|
+
color: var(--iw-color-text);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.iw-chip-picker-chips {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-wrap: wrap;
|
|
19
|
+
gap: var(--iw-space-2);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.iw-chip-picker-hint {
|
|
23
|
+
margin: var(--iw-space-2) 0 0;
|
|
24
|
+
font-size: var(--iw-font-size-xs);
|
|
25
|
+
color: var(--iw-color-text-muted, var(--iw-color-text));
|
|
26
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('../../../types/controls/chip-picker').ChipPickerProps} ChipPickerProps
|
|
3
|
+
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { html } from "@inglorious/web"
|
|
7
|
+
import { when } from "@inglorious/web/directives/when"
|
|
8
|
+
|
|
9
|
+
import { Chip } from "../../data-display/chip/index.js"
|
|
10
|
+
import { formatOption } from "../combobox/helpers.js"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Renders a group of toggle chips for picking several options, as a fieldset
|
|
14
|
+
* whose legend is `label`.
|
|
15
|
+
* @param {ChipPickerProps} props
|
|
16
|
+
* @returns {TemplateResult}
|
|
17
|
+
*/
|
|
18
|
+
export function render(props) {
|
|
19
|
+
const {
|
|
20
|
+
label,
|
|
21
|
+
hint,
|
|
22
|
+
options = [],
|
|
23
|
+
value = [],
|
|
24
|
+
isDisabled = false,
|
|
25
|
+
className = "",
|
|
26
|
+
onToggle,
|
|
27
|
+
} = props
|
|
28
|
+
|
|
29
|
+
return html`<fieldset
|
|
30
|
+
class=${["iw-chip-picker", className].filter(Boolean).join(" ")}
|
|
31
|
+
>
|
|
32
|
+
${when(
|
|
33
|
+
label,
|
|
34
|
+
() => html`<legend class="iw-chip-picker-legend">${label}</legend>`,
|
|
35
|
+
)}
|
|
36
|
+
<div class="iw-chip-picker-chips">
|
|
37
|
+
${options.map((option) => {
|
|
38
|
+
const {
|
|
39
|
+
value: optionValue,
|
|
40
|
+
label: optionLabel,
|
|
41
|
+
...rest
|
|
42
|
+
} = formatOption(option)
|
|
43
|
+
return Chip.render({
|
|
44
|
+
children: optionLabel,
|
|
45
|
+
isSelected: value.includes(optionValue),
|
|
46
|
+
isDisabled: isDisabled || Boolean(rest.isDisabled),
|
|
47
|
+
onClick: () => onToggle?.(optionValue),
|
|
48
|
+
})
|
|
49
|
+
})}
|
|
50
|
+
</div>
|
|
51
|
+
${when(hint, () => html`<p class="iw-chip-picker-hint">${hint}</p>`)}
|
|
52
|
+
</fieldset>`
|
|
53
|
+
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { formatOption, getOptionValue, visibleOptions } from "./helpers.js"
|
|
7
7
|
|
|
8
|
+
const NONE = -1
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* Initialize missing select state defaults.
|
|
10
12
|
* @param {SelectEntity} entity
|
|
@@ -16,7 +18,7 @@ export function create(entity) {
|
|
|
16
18
|
|
|
17
19
|
entity.isOpen ??= false
|
|
18
20
|
entity.searchTerm ??= ""
|
|
19
|
-
entity.focusedIndex ??=
|
|
21
|
+
entity.focusedIndex ??= NONE
|
|
20
22
|
|
|
21
23
|
entity.isMulti ??= false
|
|
22
24
|
entity.selectedValue ??= entity.isMulti ? [] : null
|
|
@@ -38,15 +40,14 @@ export function create(entity) {
|
|
|
38
40
|
export function open(entity) {
|
|
39
41
|
if (entity.isDisabled) return
|
|
40
42
|
entity.isOpen = true
|
|
41
|
-
|
|
42
|
-
const filtered = visibleOptions(entity)
|
|
43
|
-
entity.focusedIndex = filtered.length ? 0 : -1
|
|
43
|
+
entity.focusedIndex = enabledIndex(visibleOptions(entity), 0, 1)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/** @param {SelectEntity} entity */
|
|
47
47
|
export function close(entity) {
|
|
48
48
|
entity.isOpen = false
|
|
49
|
-
entity.
|
|
49
|
+
entity.searchTerm = ""
|
|
50
|
+
entity.focusedIndex = NONE
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
/** @param {SelectEntity} entity */
|
|
@@ -60,15 +61,16 @@ export function toggle(entity) {
|
|
|
60
61
|
* @param {SelectEntity} entity
|
|
61
62
|
* @param {SelectOption} option
|
|
62
63
|
*/
|
|
63
|
-
export function optionSelect(entity, option) {
|
|
64
|
+
export function optionSelect(entity, option, api) {
|
|
64
65
|
if (entity.isDisabled) return
|
|
65
66
|
|
|
66
67
|
const normalizedOption = formatOption(option)
|
|
68
|
+
if (normalizedOption.isDisabled) return
|
|
67
69
|
const optionValue = getOptionValue(normalizedOption)
|
|
68
70
|
|
|
69
|
-
if (normalizedOption.isCreated) {
|
|
71
|
+
if (normalizedOption.isCreated || entity.searchTerm) {
|
|
70
72
|
entity.searchTerm = ""
|
|
71
|
-
entity.focusedIndex =
|
|
73
|
+
entity.focusedIndex = NONE
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
if (entity.isMulti) {
|
|
@@ -84,17 +86,24 @@ export function optionSelect(entity, option) {
|
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
entity.selectedValue = values
|
|
89
|
+
notifyChange(entity, api)
|
|
87
90
|
return
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
entity.selectedValue = optionValue
|
|
91
94
|
close(entity)
|
|
95
|
+
notifyChange(entity, api)
|
|
92
96
|
}
|
|
93
97
|
|
|
94
|
-
/**
|
|
95
|
-
|
|
98
|
+
/**
|
|
99
|
+
* @param {SelectEntity} entity
|
|
100
|
+
* @param {unknown} _payload
|
|
101
|
+
* @param {import('@inglorious/web').Api} [api]
|
|
102
|
+
*/
|
|
103
|
+
export function clear(entity, _payload, api) {
|
|
96
104
|
if (entity.isDisabled) return
|
|
97
105
|
entity.selectedValue = entity.isMulti ? [] : null
|
|
106
|
+
notifyChange(entity, api)
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
/**
|
|
@@ -103,32 +112,36 @@ export function clear(entity) {
|
|
|
103
112
|
*/
|
|
104
113
|
export function searchChange(entity, searchTerm) {
|
|
105
114
|
entity.searchTerm = searchTerm
|
|
106
|
-
|
|
107
|
-
entity.focusedIndex = filtered.length ? 0 : -1
|
|
115
|
+
entity.focusedIndex = enabledIndex(visibleOptions(entity), 0, 1)
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
/** @param {SelectEntity} entity */
|
|
111
119
|
export function focusNext(entity) {
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
114
|
-
entity.focusedIndex = Math.min(entity.focusedIndex + 1, filtered.length - 1)
|
|
120
|
+
const next = enabledIndex(visibleOptions(entity), entity.focusedIndex + 1, 1)
|
|
121
|
+
if (next !== NONE) entity.focusedIndex = next
|
|
115
122
|
}
|
|
116
123
|
|
|
117
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* Before the first enabled option, focus goes back to the search box.
|
|
126
|
+
* @param {SelectEntity} entity
|
|
127
|
+
*/
|
|
118
128
|
export function focusPrev(entity) {
|
|
119
|
-
entity.focusedIndex =
|
|
129
|
+
entity.focusedIndex = enabledIndex(
|
|
130
|
+
visibleOptions(entity),
|
|
131
|
+
entity.focusedIndex - 1,
|
|
132
|
+
-1,
|
|
133
|
+
)
|
|
120
134
|
}
|
|
121
135
|
|
|
122
136
|
/** @param {SelectEntity} entity */
|
|
123
137
|
export function focusFirst(entity) {
|
|
124
|
-
|
|
125
|
-
if (filtered.length) entity.focusedIndex = 0
|
|
138
|
+
entity.focusedIndex = enabledIndex(visibleOptions(entity), 0, 1)
|
|
126
139
|
}
|
|
127
140
|
|
|
128
141
|
/** @param {SelectEntity} entity */
|
|
129
142
|
export function focusLast(entity) {
|
|
130
143
|
const filtered = visibleOptions(entity)
|
|
131
|
-
|
|
144
|
+
entity.focusedIndex = enabledIndex(filtered, filtered.length - 1, -1)
|
|
132
145
|
}
|
|
133
146
|
|
|
134
147
|
/**
|
|
@@ -139,3 +152,53 @@ export function focusLast(entity) {
|
|
|
139
152
|
export function focusSet(entity, index) {
|
|
140
153
|
entity.focusedIndex = Number.isInteger(index) ? index : entity.focusedIndex
|
|
141
154
|
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Sets the value from outside, such as a form being filled with saved data.
|
|
158
|
+
* An empty string, as an empty form field holds, is no value. Unlike a
|
|
159
|
+
* choice, it does not notify `changeEvent`.
|
|
160
|
+
* @param {SelectEntity} entity
|
|
161
|
+
* @param {string | number | (string | number)[] | null} value
|
|
162
|
+
*/
|
|
163
|
+
export function valueSet(entity, value) {
|
|
164
|
+
if (entity.isMulti) entity.selectedValue = [...(value ?? [])]
|
|
165
|
+
else entity.selectedValue = value === "" ? null : (value ?? null)
|
|
166
|
+
entity.searchTerm = ""
|
|
167
|
+
entity.focusedIndex = NONE
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Replaces the options, such as when they depend on another field.
|
|
172
|
+
* @param {SelectEntity} entity
|
|
173
|
+
* @param {SelectOption[]} options
|
|
174
|
+
*/
|
|
175
|
+
export function optionsSet(entity, options) {
|
|
176
|
+
entity.options = (options ?? []).map(formatOption)
|
|
177
|
+
entity.searchTerm = ""
|
|
178
|
+
entity.focusedIndex = NONE
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Shows a validation message. The combobox is its own entity, so the form
|
|
183
|
+
* that validates it hands the message over.
|
|
184
|
+
* @param {SelectEntity} entity
|
|
185
|
+
* @param {string | null} error
|
|
186
|
+
*/
|
|
187
|
+
export function errorSet(entity, error) {
|
|
188
|
+
entity.error = error ?? null
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function notifyChange(entity, api) {
|
|
192
|
+
if (!entity.changeEvent || !api) return
|
|
193
|
+
const value = entity.isMulti
|
|
194
|
+
? [...entity.selectedValue]
|
|
195
|
+
: entity.selectedValue
|
|
196
|
+
api.notify(entity.changeEvent, { ...entity.changePayload, value })
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function enabledIndex(options, start, step) {
|
|
200
|
+
for (let index = start; index >= 0 && index < options.length; index += step) {
|
|
201
|
+
if (!formatOption(options[index]).isDisabled) return index
|
|
202
|
+
}
|
|
203
|
+
return NONE
|
|
204
|
+
}
|
|
@@ -12,6 +12,7 @@ import { when } from "@inglorious/web/directives/when"
|
|
|
12
12
|
|
|
13
13
|
import { Chip } from "../../data-display/chip/index.js"
|
|
14
14
|
import { fallbackLabel } from "../../shared/fallback-label.js"
|
|
15
|
+
import { liveBoolean } from "../../shared/live-boolean.js"
|
|
15
16
|
import { syncPopover } from "../../shared/top-layer.js"
|
|
16
17
|
import {
|
|
17
18
|
formatOption,
|
|
@@ -384,7 +385,8 @@ export const Combobox = {
|
|
|
384
385
|
() =>
|
|
385
386
|
html`<input
|
|
386
387
|
type="checkbox"
|
|
387
|
-
|
|
388
|
+
?checked=${isSelected}
|
|
389
|
+
${liveBoolean("checked", isSelected)}
|
|
388
390
|
?disabled=${!!normalized.isDisabled}
|
|
389
391
|
/>`,
|
|
390
392
|
)}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
@import "./button/style.css";
|
|
2
1
|
@import "./button-group/style.css";
|
|
2
|
+
@import "./button/style.css";
|
|
3
3
|
@import "./checkbox/style.css";
|
|
4
|
+
@import "./chip-picker/style.css";
|
|
4
5
|
@import "./combobox/style.css";
|
|
5
6
|
@import "./fab/style.css";
|
|
7
|
+
@import "./file-upload/style.css";
|
|
6
8
|
@import "./icon-button/style.css";
|
|
7
9
|
@import "./image-cropper/style.css";
|
|
8
10
|
@import "./input/style.css";
|
|
@@ -12,4 +14,3 @@
|
|
|
12
14
|
@import "./slider/style.css";
|
|
13
15
|
@import "./switch/style.css";
|
|
14
16
|
@import "./textarea/style.css";
|
|
15
|
-
@import "./file-upload/style.css";
|
|
@@ -9,6 +9,7 @@ import { ref } from "@inglorious/web/directives/ref"
|
|
|
9
9
|
import { when } from "@inglorious/web/directives/when"
|
|
10
10
|
|
|
11
11
|
import { applyElementProps } from "../../shared/apply-element-props.js"
|
|
12
|
+
import { liveBoolean } from "../../shared/live-boolean.js"
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Renders a group of radio options for single selection.
|
|
@@ -72,7 +73,8 @@ export function render(props) {
|
|
|
72
73
|
type="radio"
|
|
73
74
|
name=${groupName || nothing}
|
|
74
75
|
value=${optionValue}
|
|
75
|
-
|
|
76
|
+
?checked=${value === optionValue}
|
|
77
|
+
${liveBoolean("checked", value === optionValue)}
|
|
76
78
|
?disabled=${isDisabled || optionDisabled}
|
|
77
79
|
@change=${(event) => onChange?.(event.target.value)}
|
|
78
80
|
${ref((element) => applyElementProps(element, optionRest))}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import { html, nothing } from "@inglorious/web"
|
|
7
7
|
import { classMap } from "@inglorious/web/directives/class-map"
|
|
8
8
|
|
|
9
|
+
import { liveBoolean } from "../../shared/live-boolean.js"
|
|
10
|
+
|
|
9
11
|
const DEFAULT_VALUE = 0
|
|
10
12
|
const DEFAULT_MAX = 5
|
|
11
13
|
const INDEX_TO_RATING = 1
|
|
@@ -62,7 +64,8 @@ export function render(props) {
|
|
|
62
64
|
name=${groupName || nothing}
|
|
63
65
|
value=${ratingValue}
|
|
64
66
|
aria-label=${`${ratingValue} of ${max}`}
|
|
65
|
-
|
|
67
|
+
?checked=${ratingValue === value}
|
|
68
|
+
${liveBoolean("checked", ratingValue === value)}
|
|
66
69
|
@change=${() => onChange?.(ratingValue)}
|
|
67
70
|
/>
|
|
68
71
|
<span aria-hidden="true">${isActive ? symbol : emptySymbol}</span>
|
|
@@ -10,6 +10,7 @@ import { when } from "@inglorious/web/directives/when"
|
|
|
10
10
|
|
|
11
11
|
import { Icon } from "../../data-display/icon/index.js"
|
|
12
12
|
import { fallbackLabel } from "../../shared/fallback-label.js"
|
|
13
|
+
import { liveBoolean } from "../../shared/live-boolean.js"
|
|
13
14
|
import { formatOption, getOptionValue, isOptionSelected } from "./helpers.js"
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -111,10 +112,6 @@ export const Select = {
|
|
|
111
112
|
"iw-select-control-error": !!error,
|
|
112
113
|
}
|
|
113
114
|
|
|
114
|
-
const selectedOption = options.find(
|
|
115
|
-
(option) => getOptionValue(option) === value,
|
|
116
|
-
)
|
|
117
|
-
|
|
118
115
|
return html`<div class="iw-select-frame" @click=${openFromArrowStrip}>
|
|
119
116
|
${when(icon, () => Icon.render({ children: icon, size }))}
|
|
120
117
|
|
|
@@ -137,15 +134,11 @@ export const Select = {
|
|
|
137
134
|
(option) => getOptionValue(option),
|
|
138
135
|
(option) => {
|
|
139
136
|
const {
|
|
140
|
-
value,
|
|
137
|
+
value: optionValue,
|
|
141
138
|
label,
|
|
142
139
|
isDisabled: isOptionDisabled,
|
|
143
140
|
} = formatOption(option)
|
|
144
|
-
const isSelected = isOptionSelected(
|
|
145
|
-
option,
|
|
146
|
-
selectedOption?.value,
|
|
147
|
-
isMulti,
|
|
148
|
-
)
|
|
141
|
+
const isSelected = isOptionSelected(option, value, isMulti)
|
|
149
142
|
|
|
150
143
|
const optionClasses = {
|
|
151
144
|
"iw-select-option": true,
|
|
@@ -153,8 +146,9 @@ export const Select = {
|
|
|
153
146
|
}
|
|
154
147
|
|
|
155
148
|
return html`<option
|
|
156
|
-
value=${
|
|
157
|
-
|
|
149
|
+
value=${optionValue}
|
|
150
|
+
?selected=${isSelected}
|
|
151
|
+
${liveBoolean("selected", isSelected)}
|
|
158
152
|
?disabled=${isOptionDisabled}
|
|
159
153
|
class=${classMap(optionClasses)}
|
|
160
154
|
>
|
|
@@ -9,6 +9,7 @@ import { ref } from "@inglorious/web/directives/ref"
|
|
|
9
9
|
import { when } from "@inglorious/web/directives/when"
|
|
10
10
|
|
|
11
11
|
import { applyElementProps } from "../../shared/apply-element-props.js"
|
|
12
|
+
import { liveBoolean } from "../../shared/live-boolean.js"
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Renders a toggle switch backed by a checkbox input, which `role="switch"`
|
|
@@ -49,7 +50,8 @@ export function render(props) {
|
|
|
49
50
|
type="checkbox"
|
|
50
51
|
role="switch"
|
|
51
52
|
name=${name}
|
|
52
|
-
|
|
53
|
+
?checked=${isChecked}
|
|
54
|
+
${liveBoolean("checked", isChecked)}
|
|
53
55
|
?disabled=${isDisabled}
|
|
54
56
|
@change=${(event) => onChange?.(event.target.checked)}
|
|
55
57
|
${ref((element) => applyElementProps(element, rest))}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createRender } from "../../stories/notify-story.js"
|
|
2
|
+
import { Lightbox } from "."
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: "Data Display/Lightbox",
|
|
6
|
+
tags: ["autodocs"],
|
|
7
|
+
render: createRender(Lightbox),
|
|
8
|
+
argTypes: {
|
|
9
|
+
index: { control: "number" },
|
|
10
|
+
onStep: { action: "onStep" },
|
|
11
|
+
onClose: { action: "onClose" },
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Default = {
|
|
16
|
+
args: {
|
|
17
|
+
isOpen: true,
|
|
18
|
+
index: 0,
|
|
19
|
+
images: [
|
|
20
|
+
"https://picsum.photos/id/1011/1200/800",
|
|
21
|
+
"https://picsum.photos/id/1015/1200/800",
|
|
22
|
+
"https://picsum.photos/id/1016/800/1200",
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
.iw-lightbox {
|
|
2
|
+
width: 100vw;
|
|
3
|
+
max-width: none;
|
|
4
|
+
height: 100dvh;
|
|
5
|
+
max-height: none;
|
|
6
|
+
margin: 0;
|
|
7
|
+
padding: var(--iw-space-4);
|
|
8
|
+
border: none;
|
|
9
|
+
background: #0c0c0c;
|
|
10
|
+
color: #fff;
|
|
11
|
+
|
|
12
|
+
&[open] {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&::backdrop {
|
|
19
|
+
background: #0c0c0c;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.iw-button.iw-lightbox-close {
|
|
23
|
+
position: absolute;
|
|
24
|
+
top: calc(var(--iw-space-3) + var(--safe-area-top, 0px));
|
|
25
|
+
inset-inline-end: var(--iw-space-3);
|
|
26
|
+
z-index: 1;
|
|
27
|
+
width: 2.5rem;
|
|
28
|
+
height: 2.5rem;
|
|
29
|
+
background: color-mix(in srgb, #fff 14%, transparent);
|
|
30
|
+
color: #fff;
|
|
31
|
+
|
|
32
|
+
&:hover {
|
|
33
|
+
background: color-mix(in srgb, #fff 24%, transparent);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.iw-button.iw-lightbox-nav {
|
|
38
|
+
position: absolute;
|
|
39
|
+
top: 50%;
|
|
40
|
+
transform: translateY(-50%);
|
|
41
|
+
z-index: 1;
|
|
42
|
+
width: 2.5rem;
|
|
43
|
+
height: 2.5rem;
|
|
44
|
+
background: color-mix(in srgb, #000 45%, transparent);
|
|
45
|
+
color: #fff;
|
|
46
|
+
|
|
47
|
+
&:hover {
|
|
48
|
+
background: color-mix(in srgb, #000 62%, transparent);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&:dir(rtl) .iw-icon {
|
|
52
|
+
scale: -1 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.iw-lightbox-stage {
|
|
58
|
+
position: relative;
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
width: min(100%, 56rem);
|
|
63
|
+
height: min(100%, 90dvh);
|
|
64
|
+
user-select: none;
|
|
65
|
+
touch-action: pan-y;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.iw-lightbox-image {
|
|
69
|
+
display: block;
|
|
70
|
+
max-width: 100%;
|
|
71
|
+
max-height: 100%;
|
|
72
|
+
width: auto;
|
|
73
|
+
height: auto;
|
|
74
|
+
object-fit: contain;
|
|
75
|
+
border-radius: var(--iw-radius-xl);
|
|
76
|
+
pointer-events: none;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.iw-lightbox-previous {
|
|
80
|
+
inset-inline-start: var(--iw-space-2);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.iw-lightbox-next {
|
|
84
|
+
inset-inline-end: var(--iw-space-2);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.iw-lightbox-counter {
|
|
88
|
+
position: absolute;
|
|
89
|
+
inset-inline-end: var(--iw-space-3);
|
|
90
|
+
bottom: var(--iw-space-2);
|
|
91
|
+
padding: 0.125rem 0.5rem;
|
|
92
|
+
border-radius: 999px;
|
|
93
|
+
background: color-mix(in srgb, #000 50%, transparent);
|
|
94
|
+
color: #fff;
|
|
95
|
+
font-size: var(--iw-font-size-xs);
|
|
96
|
+
font-variant-numeric: tabular-nums;
|
|
97
|
+
}
|