@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.
Files changed (141) hide show
  1. package/.editorconfig +29 -0
  2. package/.eslintrc.json +27 -0
  3. package/.github/workflows/publish.yml +19 -0
  4. package/.github/workflows/release-please.yml +19 -0
  5. package/.github/workflows/test.yml +38 -0
  6. package/.husky/commit-msg +4 -0
  7. package/.husky/pre-commit +4 -0
  8. package/.prettierignore +1 -0
  9. package/.storybook/main.js +11 -0
  10. package/.storybook/preview-head.html +5 -0
  11. package/.storybook/preview.js +23 -0
  12. package/CHANGELOG.md +63 -0
  13. package/CODE_OF_CONDUCT.md +128 -0
  14. package/CONTRIBUTING.md +31 -0
  15. package/LICENSE +21 -0
  16. package/README.md +170 -0
  17. package/commitlint.config.cjs +1 -0
  18. package/dist/Button-83c6df93.js +403 -0
  19. package/dist/Checkbox.js +144 -0
  20. package/dist/CheckboxGroup.js +82 -0
  21. package/dist/Chip-60af1402.js +162 -0
  22. package/dist/ChipGroup.js +79 -0
  23. package/dist/ChipLink.js +46 -0
  24. package/dist/ChipRemovable.js +43 -0
  25. package/dist/ChipSelectable.js +92 -0
  26. package/dist/Input.js +686 -0
  27. package/dist/Radio.js +156 -0
  28. package/dist/RadioGroup.js +194 -0
  29. package/dist/Select.js +859 -0
  30. package/dist/Table-72d305d7.js +506 -0
  31. package/dist/Table.js +8 -0
  32. package/dist/defineElement-47d4f665.js +15 -0
  33. package/dist/icon-b68c7e1e.js +202 -0
  34. package/dist/index.js +21 -0
  35. package/dist/leu-checkbox-group.js +6 -0
  36. package/dist/leu-checkbox.js +6 -0
  37. package/dist/leu-chip-group.js +5 -0
  38. package/dist/leu-chip-link.js +6 -0
  39. package/dist/leu-chip-removable.js +7 -0
  40. package/dist/leu-chip-selectable.js +6 -0
  41. package/dist/leu-input.js +9 -0
  42. package/dist/leu-radio-group.js +6 -0
  43. package/dist/leu-radio.js +5 -0
  44. package/dist/leu-select.js +12 -0
  45. package/dist/leu-table.js +10 -0
  46. package/dist/theme.css +51 -0
  47. package/index.js +10 -0
  48. package/package.json +85 -0
  49. package/postcss.config.cjs +14 -0
  50. package/rollup.config.js +54 -0
  51. package/scripts/generate-component/generate.js +167 -0
  52. package/scripts/generate-component/templates/[Name].js +33 -0
  53. package/scripts/generate-component/templates/[name].css +11 -0
  54. package/scripts/generate-component/templates/[namespace]-[name].js +3 -0
  55. package/scripts/generate-component/templates/stories/[name].stories.js +13 -0
  56. package/scripts/generate-component/templates/test/[name].test.js +22 -0
  57. package/src/components/button/Button.js +150 -0
  58. package/src/components/button/button.css +232 -0
  59. package/src/components/button/leu-button.js +3 -0
  60. package/src/components/button/stories/button.stories.js +333 -0
  61. package/src/components/button/test/button.test.js +22 -0
  62. package/src/components/button-group/ButtonGroup.js +63 -0
  63. package/src/components/button-group/button-group.css +10 -0
  64. package/src/components/button-group/leu-button-group.js +3 -0
  65. package/src/components/button-group/stories/button-group.stories.js +41 -0
  66. package/src/components/button-group/test/button-group.test.js +22 -0
  67. package/src/components/checkbox/Checkbox.js +142 -0
  68. package/src/components/checkbox/CheckboxGroup.js +80 -0
  69. package/src/components/checkbox/leu-checkbox-group.js +3 -0
  70. package/src/components/checkbox/leu-checkbox.js +3 -0
  71. package/src/components/checkbox/stories/checkbox-group.stories.js +52 -0
  72. package/src/components/checkbox/stories/checkbox.stories.js +43 -0
  73. package/src/components/checkbox/test/checkbox.test.js +101 -0
  74. package/src/components/chip/Chip.js +24 -0
  75. package/src/components/chip/ChipGroup.js +71 -0
  76. package/src/components/chip/ChipLink.js +45 -0
  77. package/src/components/chip/ChipRemovable.js +42 -0
  78. package/src/components/chip/ChipSelectable.js +91 -0
  79. package/src/components/chip/chip-group.css +5 -0
  80. package/src/components/chip/chip.css +130 -0
  81. package/src/components/chip/exports.js +10 -0
  82. package/src/components/chip/leu-chip-group.js +3 -0
  83. package/src/components/chip/leu-chip-link.js +3 -0
  84. package/src/components/chip/leu-chip-removable.js +3 -0
  85. package/src/components/chip/leu-chip-selectable.js +3 -0
  86. package/src/components/chip/stories/chip-group.stories.js +99 -0
  87. package/src/components/chip/stories/chip-link.stories.js +37 -0
  88. package/src/components/chip/stories/chip-removable.stories.js +28 -0
  89. package/src/components/chip/stories/chip-selectable.stories.js +46 -0
  90. package/src/components/chip/test/chip.test.js +22 -0
  91. package/src/components/dropdown/Dropdown.js +55 -0
  92. package/src/components/dropdown/dropdown.css +17 -0
  93. package/src/components/dropdown/leu-dropdown.js +3 -0
  94. package/src/components/dropdown/stories/dropdown.stories.js +25 -0
  95. package/src/components/dropdown/test/dropdown.test.js +31 -0
  96. package/src/components/icon/icon.js +201 -0
  97. package/src/components/input/Input.js +421 -0
  98. package/src/components/input/input.css +231 -0
  99. package/src/components/input/leu-input.js +3 -0
  100. package/src/components/input/stories/input.stories.js +185 -0
  101. package/src/components/input/test/input.test.js +22 -0
  102. package/src/components/menu/Menu.js +18 -0
  103. package/src/components/menu/MenuItem.js +95 -0
  104. package/src/components/menu/leu-menu-item.js +3 -0
  105. package/src/components/menu/leu-menu.js +3 -0
  106. package/src/components/menu/menu-item.css +72 -0
  107. package/src/components/menu/menu.css +14 -0
  108. package/src/components/menu/stories/menu-item.stories.js +51 -0
  109. package/src/components/menu/stories/menu.stories.js +21 -0
  110. package/src/components/menu/test/menu.test.js +22 -0
  111. package/src/components/pagination/Pagination.js +152 -0
  112. package/src/components/pagination/leu-pagination.js +3 -0
  113. package/src/components/pagination/pagination.css +49 -0
  114. package/src/components/pagination/stories/pagination.stories.js +82 -0
  115. package/src/components/pagination/test/pagination.test.js +22 -0
  116. package/src/components/radio/Radio.js +62 -0
  117. package/src/components/radio/RadioGroup.js +193 -0
  118. package/src/components/radio/leu-radio-group.js +3 -0
  119. package/src/components/radio/leu-radio.js +3 -0
  120. package/src/components/radio/radio.css +76 -0
  121. package/src/components/radio/stories/radio-group.stories.js +49 -0
  122. package/src/components/radio/stories/radio.stories.js +48 -0
  123. package/src/components/radio/test/radio.test.js +38 -0
  124. package/src/components/select/Select.js +350 -0
  125. package/src/components/select/leu-select.js +3 -0
  126. package/src/components/select/select.css +215 -0
  127. package/src/components/select/stories/select.stories.js +302 -0
  128. package/src/components/select/test/select.test.js +29 -0
  129. package/src/components/table/Table.js +301 -0
  130. package/src/components/table/leu-table.js +3 -0
  131. package/src/components/table/stories/table.stories.js +116 -0
  132. package/src/components/table/test/table.test.js +36 -0
  133. package/src/lib/defineElement.js +13 -0
  134. package/src/lib/hasSlotController.js +85 -0
  135. package/src/styles/custom-media.css +5 -0
  136. package/src/styles/custom-properties.css +51 -0
  137. package/src/styles/theme.css +1 -0
  138. package/stat_zh.png +0 -0
  139. package/stylelint.config.mjs +21 -0
  140. package/web-dev-server-storybook.config.mjs +19 -0
  141. 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,3 @@
1
+ import { defineMenuItemElements } from "./MenuItem.js"
2
+
3
+ defineMenuItemElements()
@@ -0,0 +1,3 @@
1
+ import { defineMenuElements } from "./Menu.js"
2
+
3
+ defineMenuElements()
@@ -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,14 @@
1
+ :host,
2
+ :host * {
3
+ box-sizing: border-box;
4
+ }
5
+
6
+ :host {
7
+ --menu-divider-color: var(--leu-color-black-transp-20);
8
+ }
9
+
10
+ :host ::slotted(hr) {
11
+ border: 0;
12
+ border-top: 1px solid var(--menu-divider-color);
13
+ margin: 0.5rem 0;
14
+ }
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ import { definePaginationElements } from "./Pagination.js"
2
+
3
+ definePaginationElements()