@statistikzh/leu 0.0.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/.editorconfig +29 -0
- package/.eslintrc.json +27 -0
- package/.github/workflows/publish.yml +19 -0
- package/.github/workflows/release-please.yml +19 -0
- package/.github/workflows/test.yml +38 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +4 -0
- package/.prettierignore +1 -0
- package/.storybook/main.js +11 -0
- package/.storybook/preview-head.html +5 -0
- package/.storybook/preview.js +23 -0
- package/CHANGELOG.md +63 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +31 -0
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/commitlint.config.cjs +1 -0
- package/dist/Button-83c6df93.js +403 -0
- package/dist/Checkbox.js +144 -0
- package/dist/CheckboxGroup.js +82 -0
- package/dist/Chip-60af1402.js +162 -0
- package/dist/ChipGroup.js +79 -0
- package/dist/ChipLink.js +46 -0
- package/dist/ChipRemovable.js +43 -0
- package/dist/ChipSelectable.js +92 -0
- package/dist/Input.js +686 -0
- package/dist/Radio.js +156 -0
- package/dist/RadioGroup.js +194 -0
- package/dist/Select.js +859 -0
- package/dist/Table-72d305d7.js +506 -0
- package/dist/Table.js +8 -0
- package/dist/defineElement-47d4f665.js +15 -0
- package/dist/icon-b68c7e1e.js +202 -0
- package/dist/index.js +21 -0
- package/dist/leu-checkbox-group.js +6 -0
- package/dist/leu-checkbox.js +6 -0
- package/dist/leu-chip-group.js +5 -0
- package/dist/leu-chip-link.js +6 -0
- package/dist/leu-chip-removable.js +7 -0
- package/dist/leu-chip-selectable.js +6 -0
- package/dist/leu-input.js +9 -0
- package/dist/leu-radio-group.js +6 -0
- package/dist/leu-radio.js +5 -0
- package/dist/leu-select.js +12 -0
- package/dist/leu-table.js +10 -0
- package/dist/theme.css +51 -0
- package/index.js +10 -0
- package/package.json +85 -0
- package/postcss.config.cjs +14 -0
- package/rollup.config.js +54 -0
- package/scripts/generate-component/generate.js +167 -0
- package/scripts/generate-component/templates/[Name].js +33 -0
- package/scripts/generate-component/templates/[name].css +11 -0
- package/scripts/generate-component/templates/[namespace]-[name].js +3 -0
- package/scripts/generate-component/templates/stories/[name].stories.js +13 -0
- package/scripts/generate-component/templates/test/[name].test.js +22 -0
- package/src/components/button/Button.js +150 -0
- package/src/components/button/button.css +232 -0
- package/src/components/button/leu-button.js +3 -0
- package/src/components/button/stories/button.stories.js +333 -0
- package/src/components/button/test/button.test.js +22 -0
- package/src/components/button-group/ButtonGroup.js +63 -0
- package/src/components/button-group/button-group.css +10 -0
- package/src/components/button-group/leu-button-group.js +3 -0
- package/src/components/button-group/stories/button-group.stories.js +41 -0
- package/src/components/button-group/test/button-group.test.js +22 -0
- package/src/components/checkbox/Checkbox.js +142 -0
- package/src/components/checkbox/CheckboxGroup.js +80 -0
- package/src/components/checkbox/leu-checkbox-group.js +3 -0
- package/src/components/checkbox/leu-checkbox.js +3 -0
- package/src/components/checkbox/stories/checkbox-group.stories.js +52 -0
- package/src/components/checkbox/stories/checkbox.stories.js +43 -0
- package/src/components/checkbox/test/checkbox.test.js +101 -0
- package/src/components/chip/Chip.js +24 -0
- package/src/components/chip/ChipGroup.js +71 -0
- package/src/components/chip/ChipLink.js +45 -0
- package/src/components/chip/ChipRemovable.js +42 -0
- package/src/components/chip/ChipSelectable.js +91 -0
- package/src/components/chip/chip-group.css +5 -0
- package/src/components/chip/chip.css +130 -0
- package/src/components/chip/exports.js +10 -0
- package/src/components/chip/leu-chip-group.js +3 -0
- package/src/components/chip/leu-chip-link.js +3 -0
- package/src/components/chip/leu-chip-removable.js +3 -0
- package/src/components/chip/leu-chip-selectable.js +3 -0
- package/src/components/chip/stories/chip-group.stories.js +99 -0
- package/src/components/chip/stories/chip-link.stories.js +37 -0
- package/src/components/chip/stories/chip-removable.stories.js +28 -0
- package/src/components/chip/stories/chip-selectable.stories.js +46 -0
- package/src/components/chip/test/chip.test.js +22 -0
- package/src/components/dropdown/Dropdown.js +55 -0
- package/src/components/dropdown/dropdown.css +17 -0
- package/src/components/dropdown/leu-dropdown.js +3 -0
- package/src/components/dropdown/stories/dropdown.stories.js +25 -0
- package/src/components/dropdown/test/dropdown.test.js +31 -0
- package/src/components/icon/icon.js +201 -0
- package/src/components/input/Input.js +421 -0
- package/src/components/input/input.css +231 -0
- package/src/components/input/leu-input.js +3 -0
- package/src/components/input/stories/input.stories.js +185 -0
- package/src/components/input/test/input.test.js +22 -0
- package/src/components/menu/Menu.js +18 -0
- package/src/components/menu/MenuItem.js +95 -0
- package/src/components/menu/leu-menu-item.js +3 -0
- package/src/components/menu/leu-menu.js +3 -0
- package/src/components/menu/menu-item.css +72 -0
- package/src/components/menu/menu.css +14 -0
- package/src/components/menu/stories/menu-item.stories.js +51 -0
- package/src/components/menu/stories/menu.stories.js +21 -0
- package/src/components/menu/test/menu.test.js +22 -0
- package/src/components/pagination/Pagination.js +152 -0
- package/src/components/pagination/leu-pagination.js +3 -0
- package/src/components/pagination/pagination.css +49 -0
- package/src/components/pagination/stories/pagination.stories.js +82 -0
- package/src/components/pagination/test/pagination.test.js +22 -0
- package/src/components/radio/Radio.js +62 -0
- package/src/components/radio/RadioGroup.js +193 -0
- package/src/components/radio/leu-radio-group.js +3 -0
- package/src/components/radio/leu-radio.js +3 -0
- package/src/components/radio/radio.css +76 -0
- package/src/components/radio/stories/radio-group.stories.js +49 -0
- package/src/components/radio/stories/radio.stories.js +48 -0
- package/src/components/radio/test/radio.test.js +38 -0
- package/src/components/select/Select.js +350 -0
- package/src/components/select/leu-select.js +3 -0
- package/src/components/select/select.css +215 -0
- package/src/components/select/stories/select.stories.js +302 -0
- package/src/components/select/test/select.test.js +29 -0
- package/src/components/table/Table.js +301 -0
- package/src/components/table/leu-table.js +3 -0
- package/src/components/table/stories/table.stories.js +116 -0
- package/src/components/table/test/table.test.js +36 -0
- package/src/lib/defineElement.js +13 -0
- package/src/lib/hasSlotController.js +85 -0
- package/src/styles/custom-media.css +5 -0
- package/src/styles/custom-properties.css +51 -0
- package/src/styles/theme.css +1 -0
- package/stat_zh.png +0 -0
- package/stylelint.config.mjs +21 -0
- package/web-dev-server-storybook.config.mjs +19 -0
- package/web-test-runner.config.mjs +49 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { html } from "lit"
|
|
2
|
+
import { ifDefined } from "lit/directives/if-defined.js"
|
|
3
|
+
|
|
4
|
+
import "../leu-input.js"
|
|
5
|
+
|
|
6
|
+
import { SIZE_TYPES } from "../Input.js"
|
|
7
|
+
import { ICON_NAMES } from "../../icon/icon.js"
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
title: "Input",
|
|
11
|
+
component: "leu-input",
|
|
12
|
+
tags: ["autodocs"],
|
|
13
|
+
argTypes: {
|
|
14
|
+
size: {
|
|
15
|
+
control: {
|
|
16
|
+
type: "select",
|
|
17
|
+
options: SIZE_TYPES,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
icon: { control: "select", options: ICON_NAMES },
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function Template(args) {
|
|
25
|
+
const {
|
|
26
|
+
label,
|
|
27
|
+
value,
|
|
28
|
+
error,
|
|
29
|
+
pattern,
|
|
30
|
+
prefix,
|
|
31
|
+
suffix,
|
|
32
|
+
size,
|
|
33
|
+
icon,
|
|
34
|
+
type,
|
|
35
|
+
min,
|
|
36
|
+
max,
|
|
37
|
+
minlength,
|
|
38
|
+
maxlength,
|
|
39
|
+
disabled = false,
|
|
40
|
+
required = false,
|
|
41
|
+
clearable = false,
|
|
42
|
+
novalidate = false,
|
|
43
|
+
} = args
|
|
44
|
+
|
|
45
|
+
return html`
|
|
46
|
+
<leu-input
|
|
47
|
+
value=${ifDefined(value)}
|
|
48
|
+
error=${ifDefined(error)}
|
|
49
|
+
pattern=${ifDefined(pattern)}
|
|
50
|
+
prefix=${ifDefined(prefix)}
|
|
51
|
+
suffix=${ifDefined(suffix)}
|
|
52
|
+
size=${ifDefined(size)}
|
|
53
|
+
icon=${ifDefined(icon)}
|
|
54
|
+
type=${ifDefined(type)}
|
|
55
|
+
min=${ifDefined(min)}
|
|
56
|
+
max=${ifDefined(max)}
|
|
57
|
+
minlength=${ifDefined(minlength)}
|
|
58
|
+
maxlength=${ifDefined(maxlength)}
|
|
59
|
+
?disabled=${disabled}
|
|
60
|
+
?required=${required}
|
|
61
|
+
?clearable=${clearable}
|
|
62
|
+
?novalidate=${novalidate}
|
|
63
|
+
>
|
|
64
|
+
${label}
|
|
65
|
+
</leu-input>
|
|
66
|
+
`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const Regular = Template.bind({})
|
|
70
|
+
Regular.args = {
|
|
71
|
+
label: "Vorname",
|
|
72
|
+
}
|
|
73
|
+
Regular.parameters = {
|
|
74
|
+
docs: {
|
|
75
|
+
description: {
|
|
76
|
+
story:
|
|
77
|
+
"To render a basic input field only the `label` attribute is required. The `label` is necessary for accessibility reasons.",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const Filled = Template.bind({})
|
|
83
|
+
Filled.args = {
|
|
84
|
+
label: "Name",
|
|
85
|
+
value: "Andrea Hugentobler",
|
|
86
|
+
}
|
|
87
|
+
Filled.parameters = {
|
|
88
|
+
docs: {
|
|
89
|
+
description: {
|
|
90
|
+
story: "To supply a value to the input field, use the `value` attribute.",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const PrefixedNumber = Template.bind({})
|
|
96
|
+
PrefixedNumber.args = {
|
|
97
|
+
label: "Preis, in CHF",
|
|
98
|
+
prefix: "CHF",
|
|
99
|
+
type: "number",
|
|
100
|
+
}
|
|
101
|
+
PrefixedNumber.parameters = {
|
|
102
|
+
docs: {
|
|
103
|
+
description: {
|
|
104
|
+
story:
|
|
105
|
+
'With the `prefix` attribute you can add a string that is prepended to the input field. This is useful for defining a unit of the input value. Be aware that the prefix is not included in the value of the input field. It is also hidden from screen readers. You have to add the prefix to the `label` attribute like "Preis, in CHF".',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const SuffixedNumber = Template.bind({})
|
|
111
|
+
SuffixedNumber.args = {
|
|
112
|
+
label: "Länge, in cm",
|
|
113
|
+
suffix: "cm",
|
|
114
|
+
type: "number",
|
|
115
|
+
min: 90,
|
|
116
|
+
max: 120,
|
|
117
|
+
}
|
|
118
|
+
SuffixedNumber.parameters = {
|
|
119
|
+
docs: {
|
|
120
|
+
description: {
|
|
121
|
+
story:
|
|
122
|
+
'With the `suffix` attribute you can add a string that is appended to the input field. This is useful for defining a unit of the input value. Be aware that the prefix is not included in the value of the input field. It is also hidden from screen readers. You have to add the suffix to the `label` attribute like "Länge, in cm".',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const Disabled = Template.bind({})
|
|
128
|
+
Disabled.args = {
|
|
129
|
+
label: "Name",
|
|
130
|
+
disabled: true,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const FilledDisabled = Template.bind({})
|
|
134
|
+
FilledDisabled.args = {
|
|
135
|
+
disabled: true,
|
|
136
|
+
label: "Name",
|
|
137
|
+
value: "Andrea Hugentobler",
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const Clearable = Template.bind({})
|
|
141
|
+
Clearable.args = {
|
|
142
|
+
label: "Vorname",
|
|
143
|
+
clearable: true,
|
|
144
|
+
}
|
|
145
|
+
Clearable.parameters = {
|
|
146
|
+
docs: {
|
|
147
|
+
description: {
|
|
148
|
+
story:
|
|
149
|
+
"The `clearable` attribute adds a button to the input field that clears the value. This is useful for search fields. The button is only visible if the input field has a value. The button will also not visible if the input is invalid.",
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const Search = Template.bind({})
|
|
155
|
+
Search.args = {
|
|
156
|
+
label: "Suchen",
|
|
157
|
+
clearable: true,
|
|
158
|
+
size: SIZE_TYPES.SMALL,
|
|
159
|
+
icon: "search",
|
|
160
|
+
novalidate: true,
|
|
161
|
+
}
|
|
162
|
+
Search.parameters = {
|
|
163
|
+
docs: {
|
|
164
|
+
description: {
|
|
165
|
+
story:
|
|
166
|
+
"The input field can also be displayed in a smaller size. Search fields or input fields inside other components (e.g. Select or Pagination) usually use this option. To display the search icon, use the `icon` attribute. With `novalidate` the validation can be disabled.",
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export const CustomError = Template.bind({})
|
|
172
|
+
CustomError.args = {
|
|
173
|
+
type: "email",
|
|
174
|
+
label: "E-Mail",
|
|
175
|
+
value: "example@domain.com",
|
|
176
|
+
error: "Diese E-Mail Adresse wird bereits verwendet.",
|
|
177
|
+
}
|
|
178
|
+
Search.parameters = {
|
|
179
|
+
docs: {
|
|
180
|
+
description: {
|
|
181
|
+
story:
|
|
182
|
+
"The input component uses the browsers native validation. If the data is sent to a server, then there it will be validated again. To display those errors, use the `error` attribute. It won't interfere with the native validation also wont be removed.",
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { html } from "lit"
|
|
2
|
+
import { fixture, expect } from "@open-wc/testing"
|
|
3
|
+
|
|
4
|
+
import "../leu-input.js"
|
|
5
|
+
|
|
6
|
+
async function defaultFixture() {
|
|
7
|
+
return fixture(html` <leu-input> Name </leu-input> `)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe("LeuInput", () => {
|
|
11
|
+
it("is a defined element", async () => {
|
|
12
|
+
const el = await customElements.get("leu-input")
|
|
13
|
+
|
|
14
|
+
await expect(el).not.to.be.undefined
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it("passes the a11y audit", async () => {
|
|
18
|
+
const el = await defaultFixture()
|
|
19
|
+
|
|
20
|
+
await expect(el).shadowDom.to.be.accessible()
|
|
21
|
+
})
|
|
22
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { html, LitElement } from "lit"
|
|
2
|
+
import { defineElement } from "../../lib/defineElement.js"
|
|
3
|
+
import styles from "./menu.css"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @tagname leu-menu
|
|
7
|
+
*/
|
|
8
|
+
export class LeuMenu extends LitElement {
|
|
9
|
+
static styles = styles
|
|
10
|
+
|
|
11
|
+
render() {
|
|
12
|
+
return html`<slot></slot>`
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function defineMenuElements() {
|
|
17
|
+
defineElement("menu", LeuMenu)
|
|
18
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { html, LitElement, nothing } from "lit"
|
|
2
|
+
import { defineElement } from "../../lib/defineElement.js"
|
|
3
|
+
import styles from "./menu-item.css"
|
|
4
|
+
|
|
5
|
+
import { Icon, ICON_NAMES } from "../icon/icon.js"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @tagname leu-menu-item
|
|
9
|
+
* @slot - The label of the menu item
|
|
10
|
+
*/
|
|
11
|
+
export class LeuMenuItem extends LitElement {
|
|
12
|
+
static styles = styles
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
static shadowRootOptions = {
|
|
18
|
+
...LitElement.shadowRootOptions,
|
|
19
|
+
delegatesFocus: true,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static properties = {
|
|
23
|
+
/**
|
|
24
|
+
* Can be either an icon name or a text
|
|
25
|
+
* If no icon with this value is found, it will be displayed as text.
|
|
26
|
+
* If the value is "EMPTY", an empty placeholder with the size of an icon will be displayed.
|
|
27
|
+
*/
|
|
28
|
+
before: { type: String },
|
|
29
|
+
/**
|
|
30
|
+
* Can be either an icon name or a text
|
|
31
|
+
* If no icon with this value is found, it will be displayed as text
|
|
32
|
+
* If the value is "EMPTY", an empty placeholder with the size of an icon will be displayed.
|
|
33
|
+
*/
|
|
34
|
+
after: { type: String },
|
|
35
|
+
active: { type: Boolean, reflect: true },
|
|
36
|
+
highlighted: { type: Boolean, reflect: true },
|
|
37
|
+
disabled: { type: Boolean, reflect: true },
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
constructor() {
|
|
41
|
+
super()
|
|
42
|
+
|
|
43
|
+
this.active = false
|
|
44
|
+
this.disabled = false
|
|
45
|
+
this.before = ""
|
|
46
|
+
this.after = ""
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A programmatic way to highlight the menu item like it is hovered.
|
|
50
|
+
* This is just a visual effect and does not change the active state.
|
|
51
|
+
*/
|
|
52
|
+
this.highlighted = false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static getIconOrText(name) {
|
|
56
|
+
if (ICON_NAMES.includes(name)) {
|
|
57
|
+
return Icon(name)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (name === "EMPTY") {
|
|
61
|
+
return html`<div class="icon-placeholder"></div>`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return name
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
renderBefore() {
|
|
68
|
+
if (this.before !== "") {
|
|
69
|
+
const content = LeuMenuItem.getIconOrText(this.before)
|
|
70
|
+
return html`<span class="before">${content}</span>`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return nothing
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
renderAfter() {
|
|
77
|
+
if (this.after !== "") {
|
|
78
|
+
const content = LeuMenuItem.getIconOrText(this.after)
|
|
79
|
+
return html`<span class="after">${content}</span>`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return nothing
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
render() {
|
|
86
|
+
return html`<button class="button" ?disabled=${this.disabled}>
|
|
87
|
+
${this.renderBefore()}<span class="label"><slot></slot></span
|
|
88
|
+
>${this.renderAfter()}
|
|
89
|
+
</button>`
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function defineMenuItemElements() {
|
|
94
|
+
defineElement("menu-item", LeuMenuItem)
|
|
95
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
:host,
|
|
2
|
+
:host * {
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
:host {
|
|
7
|
+
--background: var(--leu-color-black-0);
|
|
8
|
+
--background-hover: var(--leu-color-black-10);
|
|
9
|
+
--background-active: var(--leu-color-func-cyan);
|
|
10
|
+
--background-disabled: var(--leu-color-black-0);
|
|
11
|
+
--color: var(--leu-color-black-transp-60);
|
|
12
|
+
--font-regular: var(--leu-font-regular);
|
|
13
|
+
--font-black: var(--leu-font-black);
|
|
14
|
+
|
|
15
|
+
font-family: var(--chip-font-regular);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.button {
|
|
19
|
+
appearance: none;
|
|
20
|
+
border: none;
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
gap: 0.5rem;
|
|
26
|
+
width: 100%;
|
|
27
|
+
|
|
28
|
+
padding: 0.75rem;
|
|
29
|
+
|
|
30
|
+
font-size: 1rem;
|
|
31
|
+
line-height: 1.5;
|
|
32
|
+
text-align: left;
|
|
33
|
+
|
|
34
|
+
background: var(--background);
|
|
35
|
+
color: var(--color);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.button:focus-visible {
|
|
39
|
+
outline: 2px solid var(--leu-color-func-cyan);
|
|
40
|
+
outline-offset: 2px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.button:hover,
|
|
44
|
+
:host([highlighted]) .button {
|
|
45
|
+
--background: var(--background-hover);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
:host([active]) .button {
|
|
49
|
+
--background: var(--background-active);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:host([disabled]) .button {
|
|
53
|
+
--background: var(--background-disabled);
|
|
54
|
+
cursor: default;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
:is(.before, .after) svg {
|
|
58
|
+
display: block;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.label {
|
|
62
|
+
flex: 1;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
text-overflow: ellipsis;
|
|
65
|
+
white-space: nowrap;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.icon-placeholder {
|
|
69
|
+
display: block;
|
|
70
|
+
width: 1.5rem;
|
|
71
|
+
aspect-ratio: 1;
|
|
72
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { html } from "lit"
|
|
2
|
+
import { ifDefined } from "lit/directives/if-defined.js"
|
|
3
|
+
|
|
4
|
+
import "../leu-menu-item.js"
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
title: "Menu/Item",
|
|
8
|
+
component: "leu-menu-item",
|
|
9
|
+
args: {
|
|
10
|
+
label: "Menu Item",
|
|
11
|
+
},
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function Template(args) {
|
|
15
|
+
return html`
|
|
16
|
+
<leu-menu-item
|
|
17
|
+
before=${ifDefined(args.before)}
|
|
18
|
+
after=${ifDefined(args.after)}
|
|
19
|
+
?active=${args.active}
|
|
20
|
+
>${args.label}</leu-menu-item
|
|
21
|
+
>
|
|
22
|
+
`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const Regular = Template.bind({})
|
|
26
|
+
|
|
27
|
+
export const Active = Template.bind({})
|
|
28
|
+
Active.args = {
|
|
29
|
+
active: true,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const IconBefore = Template.bind({})
|
|
33
|
+
IconBefore.args = {
|
|
34
|
+
before: "check",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const IconAfter = Template.bind({})
|
|
38
|
+
IconAfter.args = {
|
|
39
|
+
after: "arrowRight",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const IconAndTextLabel = Template.bind({})
|
|
43
|
+
IconAndTextLabel.args = {
|
|
44
|
+
before: "pin",
|
|
45
|
+
after: "CH",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const IconPlaceholder = Template.bind({})
|
|
49
|
+
IconPlaceholder.args = {
|
|
50
|
+
before: "EMPTY",
|
|
51
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { html } from "lit"
|
|
2
|
+
import "../leu-menu.js"
|
|
3
|
+
import "../leu-menu-item.js"
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: "Menu",
|
|
7
|
+
component: "leu-menu",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function Template() {
|
|
11
|
+
return html` <leu-menu>
|
|
12
|
+
<leu-menu-item before="EMPTY">Menu Item 1</leu-menu-item>
|
|
13
|
+
<leu-menu-item before="check" active>Menu Item 2</leu-menu-item>
|
|
14
|
+
<leu-menu-item before="EMPTY">Menu Item 3</leu-menu-item>
|
|
15
|
+
<hr />
|
|
16
|
+
<leu-menu-item before="pin" after="CH">Menu Item 3</leu-menu-item>
|
|
17
|
+
<leu-menu-item>Menu Item 4</leu-menu-item>
|
|
18
|
+
</leu-menu>`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const Menu = Template.bind({})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { html } from "lit"
|
|
2
|
+
import { fixture, expect } from "@open-wc/testing"
|
|
3
|
+
|
|
4
|
+
import "../leu-menu.js"
|
|
5
|
+
|
|
6
|
+
async function defaultFixture() {
|
|
7
|
+
return fixture(html` <leu-menu /> `)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe("LeuMenu", () => {
|
|
11
|
+
it("is a defined element", async () => {
|
|
12
|
+
const el = await customElements.get("leu-menu")
|
|
13
|
+
|
|
14
|
+
await expect(el).not.to.be.undefined
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it("passes the a11y audit", async () => {
|
|
18
|
+
const el = await defaultFixture()
|
|
19
|
+
|
|
20
|
+
await expect(el).shadowDom.to.be.accessible()
|
|
21
|
+
})
|
|
22
|
+
})
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { html, LitElement } from "lit"
|
|
2
|
+
import { defineElement } from "../../lib/defineElement.js"
|
|
3
|
+
import { defineButtonElements } from "../button/Button.js"
|
|
4
|
+
|
|
5
|
+
import styles from "./pagination.css"
|
|
6
|
+
|
|
7
|
+
const MIN_PAGE = 1
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @tagname leu-pagination
|
|
11
|
+
*/
|
|
12
|
+
export class LeuPagination extends LitElement {
|
|
13
|
+
static styles = styles
|
|
14
|
+
|
|
15
|
+
static events = {
|
|
16
|
+
range: {},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static properties = {
|
|
20
|
+
page: { type: Number, reflect: true },
|
|
21
|
+
itemsOnAPage: { type: Number },
|
|
22
|
+
dataLength: { type: Number },
|
|
23
|
+
|
|
24
|
+
_minPage: { type: Number, state: true },
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
constructor() {
|
|
28
|
+
super()
|
|
29
|
+
/** @type {number} */
|
|
30
|
+
this.page = 1
|
|
31
|
+
/** @type {number} */
|
|
32
|
+
this.dataLength = 0
|
|
33
|
+
/** @type {number} */
|
|
34
|
+
this.itemsOnAPage = 30
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get maxPage() {
|
|
38
|
+
return Math.ceil(this.dataLength / this.itemsOnAPage)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get firstPage() {
|
|
42
|
+
return this.page === MIN_PAGE
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get lastPage() {
|
|
46
|
+
return this.page === this.maxPage
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
holdInRange(value) {
|
|
50
|
+
return Math.min(Math.max(value, MIN_PAGE), this.maxPage)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
numberUpdate(number) {
|
|
54
|
+
this.page = this.holdInRange(number)
|
|
55
|
+
|
|
56
|
+
const min = (this.page - 1) * this.itemsOnAPage
|
|
57
|
+
const max = Math.min(min + this.itemsOnAPage, this.dataLength)
|
|
58
|
+
this.dispatchEvent(
|
|
59
|
+
new CustomEvent("range-updated", {
|
|
60
|
+
detail: {
|
|
61
|
+
min,
|
|
62
|
+
max,
|
|
63
|
+
},
|
|
64
|
+
bubbles: false,
|
|
65
|
+
})
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
change(event) {
|
|
70
|
+
// target.value = this.page // eslint-disable-line
|
|
71
|
+
this.numberUpdate(parseInt(event.target.value, 10) || 0)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
input(event) {
|
|
75
|
+
if (event.target.value !== "") {
|
|
76
|
+
event.preventDefault()
|
|
77
|
+
this.change(event)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
keydown(event) {
|
|
82
|
+
const specialKeys = [
|
|
83
|
+
"ArrowUp",
|
|
84
|
+
"ArrowDown",
|
|
85
|
+
"ArrowLeft",
|
|
86
|
+
"ArrowRight",
|
|
87
|
+
"Backspace",
|
|
88
|
+
"Enter",
|
|
89
|
+
"Tab",
|
|
90
|
+
]
|
|
91
|
+
const numberKeys = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
|
92
|
+
if (!numberKeys.includes(event.key) && !specialKeys.includes(event.key)) {
|
|
93
|
+
event.preventDefault()
|
|
94
|
+
} else {
|
|
95
|
+
if (event.key === "ArrowUp") {
|
|
96
|
+
event.preventDefault()
|
|
97
|
+
this.numberUpdate(this.page + 1)
|
|
98
|
+
}
|
|
99
|
+
if (event.key === "ArrowDown") {
|
|
100
|
+
event.preventDefault()
|
|
101
|
+
this.numberUpdate(this.page - 1)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
firstUpdated() {
|
|
107
|
+
this.numberUpdate(this.page)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
requestUpdate(name, oldValue, newValue) {
|
|
111
|
+
if (name === "itemsOnAPage") {
|
|
112
|
+
this.numberUpdate(this.page)
|
|
113
|
+
}
|
|
114
|
+
return super.requestUpdate(name, oldValue, newValue)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
render() {
|
|
118
|
+
return html`
|
|
119
|
+
<input
|
|
120
|
+
class="input"
|
|
121
|
+
.value=${this.page}
|
|
122
|
+
@input=${this.input}
|
|
123
|
+
@change=${this.change}
|
|
124
|
+
@keydown=${this.keydown}
|
|
125
|
+
type="number"
|
|
126
|
+
/>
|
|
127
|
+
<div class="label">von ${this.maxPage}</div>
|
|
128
|
+
<leu-button
|
|
129
|
+
icon="angleLeft"
|
|
130
|
+
variant="secondary"
|
|
131
|
+
@click=${(_) => {
|
|
132
|
+
this.numberUpdate(this.page - 1)
|
|
133
|
+
}}
|
|
134
|
+
?disabled=${this.firstPage}
|
|
135
|
+
></leu-button>
|
|
136
|
+
<leu-button
|
|
137
|
+
icon="angleRight"
|
|
138
|
+
variant="secondary"
|
|
139
|
+
@click=${(_) => {
|
|
140
|
+
this.numberUpdate(this.page + 1)
|
|
141
|
+
}}
|
|
142
|
+
?disabled=${this.lastPage}
|
|
143
|
+
style="margin-left:4px;"
|
|
144
|
+
></leu-button>
|
|
145
|
+
`
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function definePaginationElements() {
|
|
150
|
+
defineButtonElements()
|
|
151
|
+
defineElement("pagination", LeuPagination)
|
|
152
|
+
}
|