@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,80 @@
1
+ import { html, css, LitElement } from "lit"
2
+ import { classMap } from "lit/directives/class-map.js"
3
+ import { defineElement } from "../../lib/defineElement.js"
4
+
5
+ /**
6
+ * @tagname leu-checkbox-group
7
+ */
8
+ export class LeuCheckboxGroup extends LitElement {
9
+ static styles = css`
10
+ :host {
11
+ --group-font-regular: var(--leu-font-regular);
12
+ --group-font-black: var(--leu-font-black);
13
+
14
+ font-family: var(--group-font-regular);
15
+ }
16
+
17
+ .fieldset {
18
+ display: flex;
19
+ align-items: flex-start;
20
+ flex-wrap: wrap;
21
+ gap: 0.5rem 1rem;
22
+
23
+ border: none;
24
+ padding: 0;
25
+ }
26
+
27
+ .fieldset--vertical {
28
+ flex-direction: column;
29
+ gap: 1rem;
30
+ }
31
+
32
+ .legend {
33
+ font-family: var(--group-font-black);
34
+ font-size: 1.125rem;
35
+ line-height: 1.5;
36
+
37
+ margin-bottom: 0.5rem;
38
+ }
39
+ `
40
+
41
+ static properties = {
42
+ orientation: { type: String },
43
+ }
44
+
45
+ constructor() {
46
+ super()
47
+ this.orientation = "HORIZONTAL"
48
+ this.items = []
49
+ }
50
+
51
+ get value() {
52
+ return this.items.filter((i) => i.checked).map((i) => i.value)
53
+ }
54
+
55
+ handleSlotChange() {
56
+ this.handleItems()
57
+ }
58
+
59
+ handleItems() {
60
+ this.items = [...this.querySelectorAll(":scope > *:not([slot])")]
61
+ }
62
+
63
+ render() {
64
+ const fieldsetClasses = {
65
+ fieldset: "true",
66
+ "fieldset--vertical": this.orientation === "VERTICAL",
67
+ }
68
+
69
+ return html`
70
+ <fieldset class=${classMap(fieldsetClasses)}>
71
+ <legend class="legend"><slot name="legend"></slot></legend>
72
+ <slot @slotchange=${this.handleSlotChange}></slot>
73
+ </fieldset>
74
+ `
75
+ }
76
+ }
77
+
78
+ export function defineCheckboxGroupElements() {
79
+ defineElement("checkbox-group", LeuCheckboxGroup)
80
+ }
@@ -0,0 +1,3 @@
1
+ import { defineCheckboxGroupElements } from "./CheckboxGroup.js"
2
+
3
+ defineCheckboxGroupElements()
@@ -0,0 +1,3 @@
1
+ import { defineCheckboxElements } from "./Checkbox.js"
2
+
3
+ defineCheckboxElements()
@@ -0,0 +1,52 @@
1
+ import { html } from "lit"
2
+ import "../leu-checkbox.js"
3
+ import "../leu-checkbox-group.js"
4
+
5
+ export default {
6
+ title: "Checkbox/Group",
7
+ component: "leu-checkbox-group",
8
+ argTypes: {
9
+ legend: { control: "text" },
10
+ orientation: {
11
+ options: ["VERTICAL", "HORIZONTAL"],
12
+ control: { type: "checkbox" },
13
+ },
14
+ },
15
+ }
16
+
17
+ function Template({ legend, orientation }) {
18
+ return html`
19
+ <leu-checkbox-group orientation=${orientation}>
20
+ <span slot="legend">${legend}</span>
21
+ <leu-checkbox identifier="1" value="1" name="checkbox-button" disabled
22
+ >Kurz</leu-checkbox
23
+ >
24
+ <leu-checkbox identifier="2" value="2" name="checkbox-button"
25
+ >Etwas Länger</leu-checkbox
26
+ >
27
+ <leu-checkbox identifier="3" value="3" name="checkbox-button" disabled
28
+ >Deaktiviert dazwischen</leu-checkbox
29
+ >
30
+ <leu-checkbox identifier="4" value="4" name="checkbox-button"
31
+ >Ein langes Label um sicher ein umbruch zu erzwingen</leu-checkbox
32
+ >
33
+ </leu-checkbox-group>
34
+ `
35
+ }
36
+
37
+ export const Horizontal = Template.bind({})
38
+ export const HorizontalLegend = Template.bind({})
39
+ HorizontalLegend.args = {
40
+ legend: "Anrede",
41
+ }
42
+
43
+ export const Vertical = Template.bind({})
44
+ Vertical.args = {
45
+ orientation: "VERTICAL",
46
+ }
47
+
48
+ export const VerticalLegend = Template.bind({})
49
+ VerticalLegend.args = {
50
+ orientation: "VERTICAL",
51
+ legend: "Anrede",
52
+ }
@@ -0,0 +1,43 @@
1
+ import { html } from "lit"
2
+ import "../leu-checkbox.js"
3
+
4
+ export default {
5
+ title: "Checkbox",
6
+ component: "leu-checkbox",
7
+ argTypes: {
8
+ label: {
9
+ control: "text",
10
+ },
11
+ },
12
+ }
13
+
14
+ function Template({ label = "Label", value, checked, disabled }) {
15
+ return html`
16
+ <leu-checkbox
17
+ .value=${value}
18
+ ?checked=${checked}
19
+ ?disabled=${disabled}
20
+ identifier="checkbox-1"
21
+ >
22
+ ${label}
23
+ </leu-checkbox>
24
+ `
25
+ }
26
+
27
+ export const Regular = Template.bind({})
28
+
29
+ export const Checked = Template.bind({})
30
+ Checked.args = {
31
+ checked: true,
32
+ }
33
+
34
+ export const Disabled = Template.bind({})
35
+ Disabled.args = {
36
+ disabled: true,
37
+ }
38
+
39
+ export const CheckedDisabled = Template.bind({})
40
+ CheckedDisabled.args = {
41
+ checked: true,
42
+ disabled: true,
43
+ }
@@ -0,0 +1,101 @@
1
+ import { html } from "lit"
2
+ import { fixture, expect } from "@open-wc/testing"
3
+
4
+ import "../leu-checkbox.js"
5
+ import "../leu-checkbox-group.js"
6
+
7
+ async function defaultFixture() {
8
+ return fixture(html`
9
+ <leu-checkbox-group>
10
+ <leu-checkbox identifier="a" value="1" disabled>1</leu-checkbox>
11
+ <leu-checkbox identifier="b" value="2">2</leu-checkbox>
12
+ <leu-checkbox identifier="c" value="3">3</leu-checkbox>
13
+ </leu-checkbox-group>
14
+ `)
15
+ }
16
+
17
+ async function checkedFixture() {
18
+ return fixture(html`
19
+ <leu-checkbox-group>
20
+ <leu-checkbox identifier="a" value="1" disabled>1</leu-checkbox>
21
+ <leu-checkbox identifier="b" value="2" checked>2</leu-checkbox>
22
+ <leu-checkbox identifier="c" value="3">3</leu-checkbox>
23
+ </leu-checkbox-group>
24
+ `)
25
+ }
26
+
27
+ describe("LeuCheckbox", () => {
28
+ it("is a defined element", async () => {
29
+ const el = await customElements.get("leu-checkbox")
30
+
31
+ await expect(el).not.to.be.undefined
32
+ })
33
+
34
+ it("passes the a11y audit", async () => {
35
+ const el = await defaultFixture()
36
+
37
+ await expect(el).shadowDom.to.be.accessible()
38
+ })
39
+
40
+ describe("LeuCheckboxGroup", () => {
41
+ it("is a defined element", async () => {
42
+ const el = await customElements.get("leu-checkbox-group")
43
+
44
+ await expect(el).not.to.be.undefined
45
+ })
46
+
47
+ it("should have a default value of an empty array", async () => {
48
+ const el = await defaultFixture()
49
+
50
+ expect(el.value).to.deep.equal([])
51
+ })
52
+
53
+ it("should update the value when a checkbox is checked", async () => {
54
+ const el = await defaultFixture()
55
+ const leuCheckbox = el.querySelector('leu-checkbox[value="2"]')
56
+ const checkbox = leuCheckbox.shadowRoot.querySelector("input")
57
+
58
+ checkbox.click()
59
+
60
+ expect(el.value).to.deep.equal(["2"])
61
+ })
62
+
63
+ it("should update the value when a checkbox is unchecked", async () => {
64
+ const el = await defaultFixture()
65
+ const leuCheckbox = el.querySelector('leu-checkbox[value="2"]')
66
+ const checkbox = leuCheckbox.shadowRoot.querySelector("input")
67
+
68
+ checkbox.click()
69
+ checkbox.click()
70
+
71
+ expect(el.value).to.deep.equal([])
72
+ })
73
+
74
+ it("should not update the value when a disabled checkbox is clicked", async () => {
75
+ const el = await defaultFixture()
76
+ const leuCheckbox = el.querySelector('leu-checkbox[value="1"]')
77
+ const checkbox = leuCheckbox.shadowRoot.querySelector("input")
78
+
79
+ checkbox.click()
80
+
81
+ expect(el.value).to.deep.equal([])
82
+ })
83
+
84
+ it("should have the option checked by default as a value", async () => {
85
+ const el = await checkedFixture()
86
+
87
+ expect(el.value).to.deep.equal(["2"])
88
+ })
89
+
90
+ it("should delegate focus to the first active checkbox", async () => {
91
+ const el = await defaultFixture()
92
+ const leuCheckbox = el.querySelector('leu-checkbox[value="2"]')
93
+ const checkbox = leuCheckbox.shadowRoot.querySelector("input")
94
+
95
+ await leuCheckbox.focus()
96
+
97
+ expect(document.activeElement).to.equal(leuCheckbox)
98
+ expect(leuCheckbox.shadowRoot.activeElement).to.equal(checkbox)
99
+ })
100
+ })
101
+ })
@@ -0,0 +1,24 @@
1
+ import { LitElement } from "lit"
2
+ import styles from "./chip.css"
3
+
4
+ /* Design: https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=21161-184433&mode=design&t=Kjo5VDiqivihn8dh-11 */
5
+
6
+ export class LeuChipBase extends LitElement {
7
+ static styles = styles
8
+
9
+ /** @internal */
10
+ static shadowRootOptions = {
11
+ ...LitElement.shadowRootOptions,
12
+ delegatesFocus: true,
13
+ }
14
+
15
+ static properties = {
16
+ inverted: { type: Boolean },
17
+ }
18
+
19
+ constructor() {
20
+ super()
21
+
22
+ this.inverted = false
23
+ }
24
+ }
@@ -0,0 +1,71 @@
1
+ import { LitElement, html } from "lit"
2
+ import { defineElement } from "../../lib/defineElement.js"
3
+ import styles from "./chip-group.css"
4
+
5
+ /* Figma https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=131766-248643&mode=design&t=Kjo5VDiqivihn8dh-11 */
6
+
7
+ export const SELECTION_MODES = {
8
+ single: "single",
9
+ multiple: "multiple",
10
+ none: "none",
11
+ }
12
+
13
+ /**
14
+ * @slot - Place leu-chip-* elements inside this slot
15
+ * @tagname leu-chip-group
16
+ */
17
+ export class LeuChipGroup extends LitElement {
18
+ static styles = styles
19
+
20
+ static properties = {
21
+ selectionMode: { type: String, attribute: "selection-mode" },
22
+ }
23
+
24
+ constructor() {
25
+ super()
26
+
27
+ /** @internal */
28
+ this.items = []
29
+ }
30
+
31
+ connectedCallback() {
32
+ super.connectedCallback()
33
+
34
+ this.addEventListener("input", this.handleInput)
35
+ }
36
+
37
+ disconnectedCallback() {
38
+ super.disconnectedCallback()
39
+
40
+ this.removeEventListener("input", this.handleInput)
41
+ }
42
+
43
+ get value() {
44
+ return this.items.filter((i) => i.selected).map((i) => i.value)
45
+ }
46
+
47
+ /** @internal */
48
+ handleInput = (e) => {
49
+ if (this.selectionMode === SELECTION_MODES.single) {
50
+ this.items.forEach((item) => {
51
+ item.selected = item === e.target // eslint-disable-line no-param-reassign
52
+ })
53
+ }
54
+ }
55
+
56
+ /** @internal */
57
+ handleSlotChange = (e) => {
58
+ const slot = e.target
59
+ const items = slot.assignedElements({ flatten: true })
60
+
61
+ this.items = items
62
+ }
63
+
64
+ render() {
65
+ return html`<slot @slotchange=${this.handleSlotChange}></slot>`
66
+ }
67
+ }
68
+
69
+ export function defineChipGroupElements() {
70
+ defineElement("chip-group", LeuChipGroup)
71
+ }
@@ -0,0 +1,45 @@
1
+ import { html } from "lit"
2
+ import { defineElement } from "../../lib/defineElement.js"
3
+
4
+ import { LeuChipBase } from "./Chip.js"
5
+
6
+ export const SIZES = {
7
+ regular: "regular",
8
+ large: "large",
9
+ }
10
+
11
+ /**
12
+ * @slot - The content of the chip
13
+ * @tagname leu-chip-link
14
+ */
15
+ export class LeuChipLink extends LeuChipBase {
16
+ static properties = {
17
+ ...LeuChipBase.properties,
18
+
19
+ /**
20
+ * The size of the chip
21
+ * @type {keyof typeof SIZES}
22
+ */
23
+ size: { type: String },
24
+
25
+ href: { type: String, reflect: true },
26
+ }
27
+
28
+ constructor() {
29
+ super()
30
+
31
+ this.inverted = false
32
+ this.size = SIZES.regular
33
+ this.href = ""
34
+ }
35
+
36
+ render() {
37
+ return html`<a href=${this.href} class="button">
38
+ <span class="label"><slot></slot></span>
39
+ </a>`
40
+ }
41
+ }
42
+
43
+ export function defineChipLinkElements() {
44
+ defineElement("chip-link", LeuChipLink)
45
+ }
@@ -0,0 +1,42 @@
1
+ import { html } from "lit"
2
+ import { defineElement } from "../../lib/defineElement.js"
3
+
4
+ import { LeuChipBase } from "./Chip.js"
5
+ import { Icon } from "../icon/icon.js"
6
+
7
+ /**
8
+ * @slot - The content of the chip
9
+ * @tagname leu-chip-removable
10
+ * @fires remove - Dispatched when the user clicks on the chip
11
+ */
12
+ export class LeuChipRemovable extends LeuChipBase {
13
+ static properties = {
14
+ ...LeuChipBase.properties,
15
+ }
16
+
17
+ constructor() {
18
+ super()
19
+
20
+ /** @internal */
21
+ this._removeIcon = Icon("close", 16)
22
+ }
23
+
24
+ handleClick() {
25
+ const customEvent = new CustomEvent("remove", {
26
+ bubble: true,
27
+ composed: true,
28
+ })
29
+ this.dispatchEvent(customEvent)
30
+ }
31
+
32
+ render() {
33
+ return html`<button @click=${(e) => this.handleClick(e)} class="button">
34
+ <span class="label"><slot></slot></span>
35
+ <div class="icon">${this._removeIcon}</div>
36
+ </button>`
37
+ }
38
+ }
39
+
40
+ export function defineChipRemovableElements() {
41
+ defineElement("chip-removable", LeuChipRemovable)
42
+ }
@@ -0,0 +1,91 @@
1
+ import { html } from "lit"
2
+ import { defineElement } from "../../lib/defineElement.js"
3
+
4
+ import { LeuChipBase } from "./Chip.js"
5
+
6
+ export const SIZES = {
7
+ small: "small",
8
+ regular: "regular",
9
+ }
10
+
11
+ export const VARIANTS = {
12
+ default: "default",
13
+ radio: "radio",
14
+ }
15
+
16
+ /**
17
+ * A chip component that can be selected.
18
+ * @slot - The content of the chip
19
+ * @tagname leu-chip-selectable
20
+ */
21
+ export class LeuChipSelectable extends LeuChipBase {
22
+ static properties = {
23
+ ...LeuChipBase.properties,
24
+
25
+ /**
26
+ * The size of the chip. Not supported for radio variant.
27
+ * @type {keyof typeof SIZES}
28
+ * @default "regular"
29
+ */
30
+ size: { type: String },
31
+
32
+ /**
33
+ * The variant of the chip. Has an effect not only on the visual appearance but also on the behavior.
34
+ * - `default`: The chip behaves like a toggle button.
35
+ * - `radio`: The chip behaves like a radio button.
36
+ *
37
+ * @type {keyof typeof VARIANTS}
38
+ * @default "default"
39
+ */
40
+ variant: { type: String },
41
+
42
+ selected: { type: Boolean, reflect: true },
43
+ value: { type: String },
44
+ }
45
+
46
+ constructor() {
47
+ super()
48
+ this.size = SIZES.regular
49
+ this.variant = VARIANTS.toggle
50
+ this.selected = false
51
+
52
+ if (this.variant === VARIANTS.radio && this.size === SIZES.small) {
53
+ console.warn("Small size has no effect on radio variant")
54
+ }
55
+ }
56
+
57
+ handleClick() {
58
+ let nextSelectedState = this.selected
59
+
60
+ if (this.variant === VARIANTS.radio) {
61
+ nextSelectedState = true
62
+ } else {
63
+ nextSelectedState = !this.selected
64
+ }
65
+
66
+ if (nextSelectedState !== this.selected) {
67
+ this.selected = nextSelectedState
68
+ this.dispatchEvent(
69
+ new CustomEvent("input", {
70
+ detail: { selected: this.selected },
71
+ bubbles: true,
72
+ composed: true,
73
+ })
74
+ )
75
+ }
76
+ }
77
+
78
+ render() {
79
+ return html`<button
80
+ @click=${(e) => this.handleClick(e)}
81
+ class="button"
82
+ aria-selected=${this.selected ? "true" : "false"}
83
+ >
84
+ <span class="label"><slot></slot></span>
85
+ </button>`
86
+ }
87
+ }
88
+
89
+ export function defineChipSelectableElements() {
90
+ defineElement("chip-selectable", LeuChipSelectable)
91
+ }
@@ -0,0 +1,5 @@
1
+ :host {
2
+ display: flex;
3
+ flex-wrap: wrap;
4
+ gap: 0.5rem;
5
+ }
@@ -0,0 +1,130 @@
1
+ :host,
2
+ :host *,
3
+ :host *::before,
4
+ :host *::after {
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ :host {
9
+ --chip-background-color-default: var(--leu-color-black-transp-10);
10
+ --chip-background-color-hover: var(--leu-color-black-transp-20);
11
+ --chip-background-color-selected: var(--leu-color-black-100);
12
+ --chip-background-color-selected-hover: var(--leu-color-black-transp-80);
13
+
14
+ --chip-color-default: var(--leu-color-black-transp-60);
15
+ --chip-color-hover: var(--leu-color-black-100);
16
+ --chip-color-selected: var(--leu-color-black-0);
17
+
18
+ --chip-radio-border-default: var(--leu-color-black-transp-40);
19
+ --chip-radio-border-selected: var(--leu-color-black-0);
20
+ --chip-radio-background-default: var(--leu-color-black-0);
21
+ --chip-radio-background-selected: var(--leu-color-func-cyan);
22
+
23
+ --chip-font-regular: var(--leu-font-regular);
24
+ --chip-font-black: var(--leu-font-black);
25
+
26
+ --chip-background-color: var(--chip-background-color-default);
27
+ --chip-color: var(--chip-color-default);
28
+ --chip-radio-border: var(--chip-radio-border-default);
29
+ --chip-radio-background: var(--chip-radio-background-default);
30
+
31
+ font-family: var(--chip-font-regular);
32
+ }
33
+
34
+ :host([inverted]) {
35
+ --chip-background-color: var(--leu-color-black-transp-20);
36
+ --chip-background-color-hover: var(--leu-color-black-transp-40);
37
+
38
+ --chip-color-default: var(--leu-color-black-0);
39
+ --chip-color-hover: var(--leu-color-black-0);
40
+ --chip-color-selected: var(--leu-color-black-0);
41
+ }
42
+
43
+ :host([selected]) {
44
+ --chip-background-color: var(--chip-background-color-selected);
45
+ --chip-color: var(--chip-color-selected);
46
+ --chip-radio-border: var(--chip-radio-border-selected);
47
+ --chip-radio-background: var(--chip-radio-background-selected);
48
+ }
49
+
50
+ .button {
51
+ appearance: none;
52
+ border: none;
53
+ border-radius: 1rem;
54
+ background-color: var(--chip-background-color);
55
+ padding: 0.5rem 1rem;
56
+
57
+ color: var(--chip-color);
58
+ font-size: 0.875rem;
59
+ line-height: 1rem;
60
+
61
+ cursor: pointer;
62
+
63
+ display: inline-flex;
64
+ align-items: center;
65
+ gap: 0.5rem;
66
+ }
67
+
68
+ .button:hover,
69
+ .button:focus-visible {
70
+ --chip-background-color: var(--chip-background-color-hover);
71
+ --chip-color: var(--chip-color-hover);
72
+ }
73
+
74
+ .button:focus-visible {
75
+ outline: 2px solid var(--leu-color-func-cyan);
76
+ outline-offset: 2px;
77
+ }
78
+
79
+ :host([href]) .button {
80
+ border-radius: 1.25rem;
81
+ padding: 0.5rem 1rem;
82
+
83
+ font-family: var(--chip-font-black);
84
+ font-size: 1.125rem;
85
+ line-height: 1.5rem;
86
+ text-decoration: none;
87
+ }
88
+
89
+ :host([selected]) .button:hover,
90
+ :host([selected]) .button:focus-visible {
91
+ --chip-background-color: var(--chip-background-color-selected-hover);
92
+ --chip-color: var(--chip-color-selected);
93
+ }
94
+
95
+ :host([href][size="large"]) .button {
96
+ border-radius: 1.5rem;
97
+ padding: 0.3125rem 1.5rem;
98
+
99
+ font-size: 2rem;
100
+ line-height: 2.375rem;
101
+ }
102
+
103
+ :host([size="small"]:not([variant="radio"])) .button {
104
+ padding: 0.5625rem 0.75rem 0.4375rem;
105
+
106
+ font-size: 0.75rem;
107
+ line-height: 1;
108
+ }
109
+
110
+ :host([variant="radio"]) .button::before {
111
+ content: "";
112
+ width: 1rem;
113
+ height: 1rem;
114
+ background-color: var(--chip-radio-background);
115
+ border: 2px solid var(--chip-radio-border);
116
+ border-radius: 50%;
117
+ }
118
+
119
+ :host([variant="radio"][selected]) .button::before {
120
+ border-width: 3px;
121
+ }
122
+
123
+ .label {
124
+ position: relative;
125
+ top: -0.0625rem;
126
+ }
127
+
128
+ .icon svg {
129
+ display: block;
130
+ }