@inglorious/ui 0.1.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/LICENSE +9 -0
- package/package.json +142 -0
- package/src/controls/button/button.stories.js +139 -0
- package/src/controls/button/index.js +5 -0
- package/src/controls/button/style.css +148 -0
- package/src/controls/button/template.js +74 -0
- package/src/controls/button-group/button-group.stories.js +108 -0
- package/src/controls/button-group/index.js +4 -0
- package/src/controls/button-group/style.css +58 -0
- package/src/controls/button-group/template.js +117 -0
- package/src/controls/checkbox/checkbox.stories.js +47 -0
- package/src/controls/checkbox/index.js +4 -0
- package/src/controls/checkbox/style.css +33 -0
- package/src/controls/checkbox/template.js +54 -0
- package/src/controls/fab/fab.stories.js +52 -0
- package/src/controls/fab/index.js +4 -0
- package/src/controls/fab/style.css +76 -0
- package/src/controls/fab/template.js +33 -0
- package/src/controls/icon-button/icon-button.stories.js +70 -0
- package/src/controls/icon-button/index.js +4 -0
- package/src/controls/icon-button/style.css +14 -0
- package/src/controls/icon-button/template.js +47 -0
- package/src/controls/input/index.js +4 -0
- package/src/controls/input/input.stories.js +180 -0
- package/src/controls/input/style.css +121 -0
- package/src/controls/input/template.js +111 -0
- package/src/controls/radio-group/index.js +4 -0
- package/src/controls/radio-group/radio-group.stories.js +51 -0
- package/src/controls/radio-group/style.css +41 -0
- package/src/controls/radio-group/template.js +71 -0
- package/src/controls/rating/index.js +4 -0
- package/src/controls/rating/rating.stories.js +47 -0
- package/src/controls/rating/style.css +29 -0
- package/src/controls/rating/template.js +53 -0
- package/src/controls/select/handlers.js +133 -0
- package/src/controls/select/helpers.js +91 -0
- package/src/controls/select/index.js +15 -0
- package/src/controls/select/select.stories.js +90 -0
- package/src/controls/select/style.css +211 -0
- package/src/controls/select/template.js +423 -0
- package/src/controls/slider/index.js +4 -0
- package/src/controls/slider/slider.stories.js +43 -0
- package/src/controls/slider/style.css +28 -0
- package/src/controls/slider/template.js +66 -0
- package/src/controls/switch/index.js +4 -0
- package/src/controls/switch/style.css +82 -0
- package/src/controls/switch/switch.stories.js +45 -0
- package/src/controls/switch/template.js +55 -0
- package/src/index.js +14 -0
- package/src/layout/flex/flex.stories.js +103 -0
- package/src/layout/flex/index.js +4 -0
- package/src/layout/flex/style.css +103 -0
- package/src/layout/flex/template.js +41 -0
- package/src/layout/grid/grid.stories.js +82 -0
- package/src/layout/grid/index.js +4 -0
- package/src/layout/grid/style.css +51 -0
- package/src/layout/grid/template.js +47 -0
- package/src/shared/applyElementProps.js +79 -0
- package/src/shared/withRenderValidation.js +19 -0
- package/src/stories/notifyStory.js +43 -0
- package/src/themes/bootstrap.css +118 -0
- package/src/themes/inglorious.css +106 -0
- package/src/themes/material.css +124 -0
- package/src/tokens/index.css +133 -0
- package/src/widgets/card/card.stories.js +135 -0
- package/src/widgets/card/index.js +4 -0
- package/src/widgets/card/style.css +53 -0
- package/src/widgets/card/template.js +97 -0
- package/types/controls/button-group.d.ts +35 -0
- package/types/controls/button.d.ts +23 -0
- package/types/controls/checkbox.d.ts +18 -0
- package/types/controls/fab.d.ts +19 -0
- package/types/controls/icon-button.d.ts +23 -0
- package/types/controls/input.d.ts +29 -0
- package/types/controls/radio-group.d.ts +25 -0
- package/types/controls/rating.d.ts +18 -0
- package/types/controls/select.d.ts +56 -0
- package/types/controls/slider.d.ts +21 -0
- package/types/controls/switch.d.ts +17 -0
- package/types/index.d.ts +14 -0
- package/types/layout/flex.d.ts +20 -0
- package/types/layout/grid.d.ts +19 -0
- package/types/widgets/card.d.ts +21 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('../../../types/controls/switch').SwitchEntity} SwitchEntity
|
|
3
|
+
* @typedef {import('@inglorious/web').Api} Api
|
|
4
|
+
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { classMap, html, ref } from "@inglorious/web"
|
|
8
|
+
|
|
9
|
+
import { applyElementProps } from "@/shared/applyElementProps.js"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Switch control implemented with a checkbox input.
|
|
13
|
+
*
|
|
14
|
+
* @param {SwitchEntity} entity
|
|
15
|
+
* @param {Api} api
|
|
16
|
+
* @returns {TemplateResult}
|
|
17
|
+
*/
|
|
18
|
+
export function render(entity, api) {
|
|
19
|
+
const {
|
|
20
|
+
name = "",
|
|
21
|
+
label = "",
|
|
22
|
+
checked = false,
|
|
23
|
+
disabled = false,
|
|
24
|
+
size = "md",
|
|
25
|
+
...rest
|
|
26
|
+
} = entity
|
|
27
|
+
|
|
28
|
+
const inputId = entity.id || name
|
|
29
|
+
|
|
30
|
+
const classes = {
|
|
31
|
+
"iw-switch-field": true,
|
|
32
|
+
[`iw-switch-${size}`]: size !== "md",
|
|
33
|
+
"iw-switch-disabled": disabled,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return html`
|
|
37
|
+
<label class=${classMap(classes)} for=${inputId}>
|
|
38
|
+
<input
|
|
39
|
+
id=${inputId}
|
|
40
|
+
class="iw-switch-input"
|
|
41
|
+
type="checkbox"
|
|
42
|
+
name=${name}
|
|
43
|
+
.checked=${checked}
|
|
44
|
+
?disabled=${disabled}
|
|
45
|
+
@change=${(event) =>
|
|
46
|
+
api.notify(`#${entity.id}:change`, event.target.checked)}
|
|
47
|
+
${ref((element) => applyElementProps(element, rest))}
|
|
48
|
+
/>
|
|
49
|
+
<span class="iw-switch-track" aria-hidden="true">
|
|
50
|
+
<span class="iw-switch-thumb"></span>
|
|
51
|
+
</span>
|
|
52
|
+
${label ? html`<span class="iw-switch-label">${label}</span>` : null}
|
|
53
|
+
</label>
|
|
54
|
+
`
|
|
55
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { button } from "./controls/button/index.js"
|
|
2
|
+
export { buttonGroup } from "./controls/button-group/index.js"
|
|
3
|
+
export { checkbox } from "./controls/checkbox/index.js"
|
|
4
|
+
export { fab } from "./controls/fab/index.js"
|
|
5
|
+
export { iconButton } from "./controls/icon-button/index.js"
|
|
6
|
+
export { input } from "./controls/input/index.js"
|
|
7
|
+
export { radioGroup } from "./controls/radio-group/index.js"
|
|
8
|
+
export { rating } from "./controls/rating/index.js"
|
|
9
|
+
export { select } from "./controls/select/index.js"
|
|
10
|
+
export { slider } from "./controls/slider/index.js"
|
|
11
|
+
export { switchControl } from "./controls/switch/index.js"
|
|
12
|
+
export { card } from "./widgets/card/index.js"
|
|
13
|
+
export { flex } from "./layout/flex/index.js"
|
|
14
|
+
export { grid } from "./layout/grid/index.js"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { html } from "@inglorious/web"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
makeStoryRender,
|
|
5
|
+
notifyActionArgType,
|
|
6
|
+
} from "../../stories/notifyStory.js"
|
|
7
|
+
import { flex } from "."
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
title: "Layout/Flex",
|
|
11
|
+
tags: ["autodocs"],
|
|
12
|
+
render: makeStoryRender(flex, "story-flex"),
|
|
13
|
+
argTypes: {
|
|
14
|
+
...notifyActionArgType,
|
|
15
|
+
direction: {
|
|
16
|
+
control: "select",
|
|
17
|
+
options: ["row", "column", "row-reverse", "column-reverse"],
|
|
18
|
+
description: "Flex main-axis direction.",
|
|
19
|
+
},
|
|
20
|
+
wrap: {
|
|
21
|
+
control: "select",
|
|
22
|
+
options: ["nowrap", "wrap", "wrap-reverse"],
|
|
23
|
+
description: "Controls wrapping behavior of children.",
|
|
24
|
+
},
|
|
25
|
+
justify: {
|
|
26
|
+
control: "select",
|
|
27
|
+
options: ["start", "center", "end", "between", "around", "evenly"],
|
|
28
|
+
description: "Main-axis distribution of children.",
|
|
29
|
+
},
|
|
30
|
+
align: {
|
|
31
|
+
control: "select",
|
|
32
|
+
options: ["stretch", "start", "center", "end", "baseline"],
|
|
33
|
+
description: "Cross-axis alignment of children.",
|
|
34
|
+
},
|
|
35
|
+
gap: {
|
|
36
|
+
control: "select",
|
|
37
|
+
options: ["none", "sm", "md", "lg", "xl"],
|
|
38
|
+
description: "Spacing between children.",
|
|
39
|
+
},
|
|
40
|
+
inline: {
|
|
41
|
+
control: "boolean",
|
|
42
|
+
description: "Uses inline-flex instead of flex.",
|
|
43
|
+
},
|
|
44
|
+
fullWidth: {
|
|
45
|
+
control: "boolean",
|
|
46
|
+
description: "Expands container width to 100%.",
|
|
47
|
+
},
|
|
48
|
+
children: {
|
|
49
|
+
control: "object",
|
|
50
|
+
description: "Array of templates/content rendered as-is.",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
parameters: {
|
|
54
|
+
docs: {
|
|
55
|
+
description: {
|
|
56
|
+
component:
|
|
57
|
+
"Layout primitive for one-dimensional arrangement of template children.",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const Default = {}
|
|
64
|
+
Default.args = {
|
|
65
|
+
direction: "row",
|
|
66
|
+
wrap: "nowrap",
|
|
67
|
+
justify: "start",
|
|
68
|
+
align: "stretch",
|
|
69
|
+
gap: "md",
|
|
70
|
+
inline: false,
|
|
71
|
+
fullWidth: false,
|
|
72
|
+
children: [
|
|
73
|
+
html`<div>First</div>`,
|
|
74
|
+
html`<div>Second</div>`,
|
|
75
|
+
html`<div>Third</div>`,
|
|
76
|
+
],
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const Vertical = {}
|
|
80
|
+
Vertical.args = {
|
|
81
|
+
...Default.args,
|
|
82
|
+
direction: "column",
|
|
83
|
+
children: [
|
|
84
|
+
html`<div>Header</div>`,
|
|
85
|
+
html`<div>Body</div>`,
|
|
86
|
+
html`<div>Footer</div>`,
|
|
87
|
+
],
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const Wrapped = {}
|
|
91
|
+
Wrapped.args = {
|
|
92
|
+
...Default.args,
|
|
93
|
+
wrap: "wrap",
|
|
94
|
+
gap: "lg",
|
|
95
|
+
children: [
|
|
96
|
+
html`<div>One</div>`,
|
|
97
|
+
html`<div>Two</div>`,
|
|
98
|
+
html`<div>Three</div>`,
|
|
99
|
+
html`<div>Four</div>`,
|
|
100
|
+
html`<div>Five</div>`,
|
|
101
|
+
html`<div>Six</div>`,
|
|
102
|
+
],
|
|
103
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
.iw-flex {
|
|
2
|
+
display: flex;
|
|
3
|
+
|
|
4
|
+
&.iw-flex-inline {
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
&.iw-flex-full-width {
|
|
9
|
+
width: 100%;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
&.iw-flex-direction-column {
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&.iw-flex-direction-row {
|
|
17
|
+
flex-direction: row;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&.iw-flex-direction-row-reverse {
|
|
21
|
+
flex-direction: row-reverse;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&.iw-flex-direction-column-reverse {
|
|
25
|
+
flex-direction: column-reverse;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&.iw-flex-wrap-wrap {
|
|
29
|
+
flex-wrap: wrap;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&.iw-flex-wrap-nowrap {
|
|
33
|
+
flex-wrap: nowrap;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&.iw-flex-wrap-wrap-reverse {
|
|
37
|
+
flex-wrap: wrap-reverse;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&.iw-flex-justify-center {
|
|
41
|
+
justify-content: center;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&.iw-flex-justify-start {
|
|
45
|
+
justify-content: flex-start;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&.iw-flex-justify-end {
|
|
49
|
+
justify-content: flex-end;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&.iw-flex-justify-between {
|
|
53
|
+
justify-content: space-between;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&.iw-flex-justify-around {
|
|
57
|
+
justify-content: space-around;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&.iw-flex-justify-evenly {
|
|
61
|
+
justify-content: space-evenly;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
&.iw-flex-align-start {
|
|
65
|
+
align-items: flex-start;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
&.iw-flex-align-stretch {
|
|
69
|
+
align-items: stretch;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
&.iw-flex-align-center {
|
|
73
|
+
align-items: center;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
&.iw-flex-align-end {
|
|
77
|
+
align-items: flex-end;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&.iw-flex-align-baseline {
|
|
81
|
+
align-items: baseline;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
&.iw-flex-gap-none {
|
|
85
|
+
gap: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
&.iw-flex-gap-sm {
|
|
89
|
+
gap: var(--iw-space-2);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&.iw-flex-gap-md {
|
|
93
|
+
gap: var(--iw-space-4);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&.iw-flex-gap-lg {
|
|
97
|
+
gap: var(--iw-space-6);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&.iw-flex-gap-xl {
|
|
101
|
+
gap: var(--iw-space-8);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('../../../types/layout/flex').FlexEntity} FlexEntity
|
|
3
|
+
* @typedef {import('@inglorious/web').Api} Api
|
|
4
|
+
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { classMap, html } from "@inglorious/web"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Flex layout component for Inglorious Web.
|
|
11
|
+
* Children are rendered as-is (templates/content composition).
|
|
12
|
+
*
|
|
13
|
+
* @param {FlexEntity} entity
|
|
14
|
+
* @param {Api} api
|
|
15
|
+
* @returns {TemplateResult}
|
|
16
|
+
*/
|
|
17
|
+
export function render(entity) {
|
|
18
|
+
const {
|
|
19
|
+
direction = "row",
|
|
20
|
+
wrap = "nowrap",
|
|
21
|
+
justify = "start",
|
|
22
|
+
align = "stretch",
|
|
23
|
+
gap = "md",
|
|
24
|
+
inline = false,
|
|
25
|
+
fullWidth = false,
|
|
26
|
+
children = [],
|
|
27
|
+
} = entity
|
|
28
|
+
|
|
29
|
+
const classes = {
|
|
30
|
+
"iw-flex": true,
|
|
31
|
+
"iw-flex-inline": inline,
|
|
32
|
+
"iw-flex-full-width": fullWidth,
|
|
33
|
+
[`iw-flex-direction-${direction}`]: true,
|
|
34
|
+
[`iw-flex-wrap-${wrap}`]: true,
|
|
35
|
+
[`iw-flex-justify-${justify}`]: true,
|
|
36
|
+
[`iw-flex-align-${align}`]: true,
|
|
37
|
+
[`iw-flex-gap-${gap}`]: true,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return html`<div class=${classMap(classes)}>${children}</div>`
|
|
41
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { html } from "@inglorious/web"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
makeStoryRender,
|
|
5
|
+
notifyActionArgType,
|
|
6
|
+
} from "../../stories/notifyStory.js"
|
|
7
|
+
import { grid } from "."
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
title: "Layout/Grid",
|
|
11
|
+
tags: ["autodocs"],
|
|
12
|
+
render: makeStoryRender(grid, "story-grid"),
|
|
13
|
+
argTypes: {
|
|
14
|
+
...notifyActionArgType,
|
|
15
|
+
columns: {
|
|
16
|
+
control: "number",
|
|
17
|
+
description: "Fixed number of columns when auto-fit is not used.",
|
|
18
|
+
},
|
|
19
|
+
minColumnWidth: {
|
|
20
|
+
control: "text",
|
|
21
|
+
description: "Enables auto-fit columns with minmax(minColumnWidth, 1fr).",
|
|
22
|
+
},
|
|
23
|
+
gap: {
|
|
24
|
+
control: "select",
|
|
25
|
+
options: ["none", "sm", "md", "lg", "xl"],
|
|
26
|
+
description: "Spacing between grid cells.",
|
|
27
|
+
},
|
|
28
|
+
align: {
|
|
29
|
+
control: "select",
|
|
30
|
+
options: ["stretch", "start", "center", "end"],
|
|
31
|
+
description: "Cross-axis alignment for cell content.",
|
|
32
|
+
},
|
|
33
|
+
justify: {
|
|
34
|
+
control: "select",
|
|
35
|
+
options: ["stretch", "start", "center", "end"],
|
|
36
|
+
description: "Inline-axis alignment for cell content.",
|
|
37
|
+
},
|
|
38
|
+
fullWidth: {
|
|
39
|
+
control: "boolean",
|
|
40
|
+
description: "Expands container width to 100%.",
|
|
41
|
+
},
|
|
42
|
+
children: {
|
|
43
|
+
control: "object",
|
|
44
|
+
description: "Array of templates/content rendered as-is.",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
parameters: {
|
|
48
|
+
docs: {
|
|
49
|
+
description: {
|
|
50
|
+
component:
|
|
51
|
+
"Layout primitive for two-dimensional arrangement of template children.",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const Default = {}
|
|
58
|
+
Default.args = {
|
|
59
|
+
columns: 3,
|
|
60
|
+
gap: "md",
|
|
61
|
+
align: "stretch",
|
|
62
|
+
justify: "stretch",
|
|
63
|
+
fullWidth: false,
|
|
64
|
+
children: [
|
|
65
|
+
html`<div>Cell A</div>`,
|
|
66
|
+
html`<div>Cell B</div>`,
|
|
67
|
+
html`<div>Cell C</div>`,
|
|
68
|
+
],
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const AutoFit = {}
|
|
72
|
+
AutoFit.args = {
|
|
73
|
+
...Default.args,
|
|
74
|
+
minColumnWidth: "180px",
|
|
75
|
+
children: [
|
|
76
|
+
html`<div>Product</div>`,
|
|
77
|
+
html`<div>Price</div>`,
|
|
78
|
+
html`<div>Status</div>`,
|
|
79
|
+
html`<div>Actions</div>`,
|
|
80
|
+
html`<div>Notes</div>`,
|
|
81
|
+
],
|
|
82
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
.iw-grid {
|
|
2
|
+
display: grid;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.iw-grid.iw-grid-full-width {
|
|
6
|
+
width: 100%;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.iw-grid.iw-grid-gap-none {
|
|
10
|
+
gap: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.iw-grid.iw-grid-gap-sm {
|
|
14
|
+
gap: var(--iw-space-2);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.iw-grid.iw-grid-gap-md {
|
|
18
|
+
gap: var(--iw-space-4);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.iw-grid.iw-grid-gap-lg {
|
|
22
|
+
gap: var(--iw-space-6);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.iw-grid.iw-grid-gap-xl {
|
|
26
|
+
gap: var(--iw-space-8);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.iw-grid.iw-grid-align-start {
|
|
30
|
+
align-items: start;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.iw-grid.iw-grid-align-center {
|
|
34
|
+
align-items: center;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.iw-grid.iw-grid-align-end {
|
|
38
|
+
align-items: end;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.iw-grid.iw-grid-justify-start {
|
|
42
|
+
justify-items: start;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.iw-grid.iw-grid-justify-center {
|
|
46
|
+
justify-items: center;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.iw-grid.iw-grid-justify-end {
|
|
50
|
+
justify-items: end;
|
|
51
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('../../../types/layout/grid').GridEntity} GridEntity
|
|
3
|
+
* @typedef {import('@inglorious/web').Api} Api
|
|
4
|
+
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { classMap, html, styleMap } from "@inglorious/web"
|
|
8
|
+
|
|
9
|
+
const DEFAULT_COLUMNS = 2
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Grid layout component for Inglorious Web.
|
|
13
|
+
* Children are rendered as-is (templates/content composition).
|
|
14
|
+
*
|
|
15
|
+
* @param {GridEntity} entity
|
|
16
|
+
* @param {Api} _api
|
|
17
|
+
* @returns {TemplateResult}
|
|
18
|
+
*/
|
|
19
|
+
export function render(entity) {
|
|
20
|
+
const {
|
|
21
|
+
columns = DEFAULT_COLUMNS,
|
|
22
|
+
minColumnWidth,
|
|
23
|
+
gap = "md",
|
|
24
|
+
align = "stretch",
|
|
25
|
+
justify = "stretch",
|
|
26
|
+
fullWidth = false,
|
|
27
|
+
children = [],
|
|
28
|
+
} = entity
|
|
29
|
+
|
|
30
|
+
const classes = {
|
|
31
|
+
"iw-grid": true,
|
|
32
|
+
"iw-grid-full-width": fullWidth,
|
|
33
|
+
[`iw-grid-gap-${gap}`]: true,
|
|
34
|
+
[`iw-grid-align-${align}`]: align !== "stretch",
|
|
35
|
+
[`iw-grid-justify-${justify}`]: justify !== "stretch",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const styles = minColumnWidth
|
|
39
|
+
? {
|
|
40
|
+
gridTemplateColumns: `repeat(auto-fit, minmax(${minColumnWidth}, 1fr))`,
|
|
41
|
+
}
|
|
42
|
+
: { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }
|
|
43
|
+
|
|
44
|
+
return html`<div class=${classMap(classes)} style=${styleMap(styles)}>
|
|
45
|
+
${children}
|
|
46
|
+
</div>`
|
|
47
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const previousKeysByElement = new WeakMap()
|
|
2
|
+
|
|
3
|
+
function clearProp(element, key) {
|
|
4
|
+
if (key in element && !key.includes("-")) {
|
|
5
|
+
try {
|
|
6
|
+
element[key] = undefined
|
|
7
|
+
} catch {
|
|
8
|
+
// Ignore read-only property assignments.
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
element.removeAttribute(key)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function setProp(element, key, value) {
|
|
15
|
+
if (value == null) {
|
|
16
|
+
clearProp(element, key)
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (typeof value === "boolean") {
|
|
21
|
+
if (value) {
|
|
22
|
+
element.setAttribute(key, "")
|
|
23
|
+
} else {
|
|
24
|
+
element.removeAttribute(key)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (key in element && !key.includes("-")) {
|
|
28
|
+
try {
|
|
29
|
+
element[key] = value
|
|
30
|
+
} catch {
|
|
31
|
+
// Ignore read-only property assignments.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (key === "style" && typeof value === "object" && value !== null) {
|
|
38
|
+
Object.assign(element.style, value)
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (key in element && !key.includes("-")) {
|
|
43
|
+
try {
|
|
44
|
+
element[key] = value
|
|
45
|
+
return
|
|
46
|
+
} catch {
|
|
47
|
+
// Fallback to attribute below.
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
element.setAttribute(key, String(value))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Apply arbitrary properties/attributes to an element.
|
|
56
|
+
* Useful when entities include extra DOM fields beyond the typed API.
|
|
57
|
+
*
|
|
58
|
+
* @param {Element | null} element
|
|
59
|
+
* @param {Record<string, unknown>} props
|
|
60
|
+
*/
|
|
61
|
+
export function applyElementProps(element, props) {
|
|
62
|
+
if (!element) return
|
|
63
|
+
|
|
64
|
+
const previousKeys = previousKeysByElement.get(element) ?? new Set()
|
|
65
|
+
const nextKeys = new Set(Object.keys(props))
|
|
66
|
+
|
|
67
|
+
for (const key of previousKeys) {
|
|
68
|
+
if (!nextKeys.has(key)) {
|
|
69
|
+
clearProp(element, key)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const [key, value] of Object.entries(props)) {
|
|
74
|
+
if (key === "class" || key === "className") continue
|
|
75
|
+
setProp(element, key, value)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
previousKeysByElement.set(element, nextKeys)
|
|
79
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function withRenderValidation(type) {
|
|
2
|
+
if (typeof type.render !== "function") {
|
|
3
|
+
throw new TypeError(
|
|
4
|
+
"withRenderValidation(type): type.render must be a function",
|
|
5
|
+
)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
...type,
|
|
10
|
+
|
|
11
|
+
render(entity, api) {
|
|
12
|
+
if (entity == null || api == null) {
|
|
13
|
+
throw new TypeError("render(entity, api): both arguments are required")
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return type.render(entity, api)
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { augmentType } from "@inglorious/store/types"
|
|
2
|
+
import { createMockApi, render } from "@inglorious/web/test"
|
|
3
|
+
|
|
4
|
+
export const notifyActionArgType = {
|
|
5
|
+
onNotify: {
|
|
6
|
+
action: "api.notify",
|
|
7
|
+
control: false,
|
|
8
|
+
table: { disable: true },
|
|
9
|
+
},
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function makeStoryRender(typeDefinition, id, options = {}) {
|
|
13
|
+
const { resolveByEntityType = false } = options
|
|
14
|
+
const type = augmentType(typeDefinition)
|
|
15
|
+
|
|
16
|
+
return (args) => {
|
|
17
|
+
const container = document.createElement("div")
|
|
18
|
+
const { onNotify, ...entityArgs } = args
|
|
19
|
+
const entity = { id, ...entityArgs }
|
|
20
|
+
const api = createInstrumentedApi(entity, onNotify)
|
|
21
|
+
|
|
22
|
+
if (resolveByEntityType && entity.type) {
|
|
23
|
+
const getType = api.getType?.bind(api)
|
|
24
|
+
api.getType = (typeName) =>
|
|
25
|
+
typeName === entity.type ? type : getType?.(typeName)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
render(type.render(entity, api), container)
|
|
29
|
+
return container
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createInstrumentedApi(entityOrState, onNotify) {
|
|
34
|
+
const api = createMockApi(entityOrState)
|
|
35
|
+
const originalNotify = api.notify.bind(api)
|
|
36
|
+
|
|
37
|
+
api.notify = (type, payload) => {
|
|
38
|
+
onNotify?.({ type, payload })
|
|
39
|
+
return originalNotify(type, payload)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return api
|
|
43
|
+
}
|