@statistikzh/leu 0.2.0 → 0.4.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.
Files changed (153) hide show
  1. package/.github/workflows/deploy-github-pages.yaml +33 -0
  2. package/.storybook/main.js +27 -1
  3. package/.storybook/manager-head.html +1 -0
  4. package/.storybook/manager.js +9 -0
  5. package/.storybook/preview-head.html +1 -1
  6. package/.storybook/preview.js +59 -5
  7. package/.storybook/static/logo.svg +19 -0
  8. package/.storybook/theme.js +7 -0
  9. package/CHANGELOG.md +43 -0
  10. package/README.md +1 -1
  11. package/dist/Button.js +39 -30
  12. package/dist/ButtonGroup.js +5 -7
  13. package/dist/Checkbox.js +107 -88
  14. package/dist/CheckboxGroup.js +43 -38
  15. package/dist/{Chip-389013ff.js → Chip-dac7337d.js} +7 -2
  16. package/dist/ChipGroup.js +4 -6
  17. package/dist/ChipLink.js +6 -8
  18. package/dist/ChipRemovable.js +4 -7
  19. package/dist/ChipSelectable.js +10 -10
  20. package/dist/Dropdown.js +57 -27
  21. package/dist/Input.js +59 -43
  22. package/dist/Menu.js +2 -5
  23. package/dist/MenuItem.js +34 -17
  24. package/dist/Pagination.js +57 -55
  25. package/dist/Radio.js +13 -10
  26. package/dist/RadioGroup.js +43 -40
  27. package/dist/Select.js +57 -44
  28. package/dist/Table.js +147 -125
  29. package/dist/{defineElement-ba770aed.js → _rollupPluginBabelHelpers-20f659f4.js} +1 -15
  30. package/dist/defineElement-47d4f665.js +15 -0
  31. package/dist/index.js +29 -19
  32. package/dist/leu-button-group.js +7 -3
  33. package/dist/leu-button.js +6 -3
  34. package/dist/leu-checkbox-group.js +6 -3
  35. package/dist/leu-checkbox.js +6 -3
  36. package/dist/leu-chip-group.js +6 -3
  37. package/dist/leu-chip-link.js +7 -4
  38. package/dist/leu-chip-removable.js +7 -4
  39. package/dist/leu-chip-selectable.js +7 -4
  40. package/dist/leu-dropdown.js +13 -3
  41. package/dist/leu-input.js +7 -3
  42. package/dist/leu-menu-item.js +8 -3
  43. package/dist/leu-menu.js +6 -3
  44. package/dist/leu-pagination.js +8 -3
  45. package/dist/leu-popup-4bf6f1f4.js +216 -0
  46. package/dist/leu-radio-group.js +6 -3
  47. package/dist/leu-radio.js +6 -3
  48. package/dist/leu-select.js +14 -3
  49. package/dist/leu-table.js +9 -3
  50. package/package.json +29 -12
  51. package/scripts/generate-component/templates/[Name].js +0 -5
  52. package/scripts/generate-component/templates/[name].css +1 -1
  53. package/scripts/generate-component/templates/[namespace]-[name].js +5 -2
  54. package/src/components/accordion/Accordion.js +3 -9
  55. package/src/components/accordion/leu-accordion.js +5 -2
  56. package/src/components/accordion/stories/accordion.stories.js +7 -4
  57. package/src/components/accordion/test/accordion.test.js +92 -2
  58. package/src/components/breadcrumb/Breadcrumb.js +310 -0
  59. package/src/components/breadcrumb/breadcrumb.css +114 -0
  60. package/src/components/breadcrumb/leu-breadcrumb.js +6 -0
  61. package/src/components/breadcrumb/stories/breadcrumb.stories.js +73 -0
  62. package/src/components/breadcrumb/test/breadcrumb.test.js +141 -0
  63. package/src/components/button/Button.js +22 -27
  64. package/src/components/button/button.css +3 -3
  65. package/src/components/button/leu-button.js +5 -2
  66. package/src/components/button/stories/button.stories.js +58 -37
  67. package/src/components/button/test/button.test.js +112 -2
  68. package/src/components/button-group/ButtonGroup.js +1 -7
  69. package/src/components/button-group/leu-button-group.js +5 -2
  70. package/src/components/button-group/stories/button-group.stories.js +6 -0
  71. package/src/components/button-group/test/button-group.test.js +79 -3
  72. package/src/components/checkbox/Checkbox.js +9 -89
  73. package/src/components/checkbox/CheckboxGroup.js +9 -39
  74. package/src/components/checkbox/checkbox-group.css +29 -0
  75. package/src/components/checkbox/checkbox.css +76 -0
  76. package/src/components/checkbox/leu-checkbox-group.js +5 -2
  77. package/src/components/checkbox/leu-checkbox.js +5 -2
  78. package/src/components/checkbox/stories/checkbox-group.stories.js +44 -21
  79. package/src/components/checkbox/stories/checkbox.stories.js +7 -1
  80. package/src/components/checkbox/test/checkbox-group.test.js +124 -0
  81. package/src/components/checkbox/test/checkbox.test.js +72 -59
  82. package/src/components/chip/Chip.js +2 -1
  83. package/src/components/chip/ChipGroup.js +1 -6
  84. package/src/components/chip/ChipLink.js +2 -8
  85. package/src/components/chip/ChipRemovable.js +1 -6
  86. package/src/components/chip/ChipSelectable.js +4 -9
  87. package/src/components/chip/exports.js +4 -10
  88. package/src/components/chip/leu-chip-group.js +5 -2
  89. package/src/components/chip/leu-chip-link.js +5 -2
  90. package/src/components/chip/leu-chip-removable.js +5 -2
  91. package/src/components/chip/leu-chip-selectable.js +5 -2
  92. package/src/components/chip/stories/chip-group.stories.js +18 -6
  93. package/src/components/chip/stories/chip-link.stories.js +16 -4
  94. package/src/components/chip/stories/chip-removable.stories.js +15 -4
  95. package/src/components/chip/stories/chip-selectable.stories.js +13 -3
  96. package/src/components/chip/test/chip-group.test.js +124 -0
  97. package/src/components/chip/test/chip-link.test.js +58 -0
  98. package/src/components/chip/test/chip-removable.test.js +79 -0
  99. package/src/components/chip/test/chip-selectable.test.js +95 -0
  100. package/src/components/chip/test/chip.test.js +1 -1
  101. package/src/components/dropdown/Dropdown.js +53 -25
  102. package/src/components/dropdown/dropdown.css +1 -2
  103. package/src/components/dropdown/leu-dropdown.js +5 -2
  104. package/src/components/dropdown/stories/dropdown.stories.js +11 -5
  105. package/src/components/dropdown/test/dropdown.test.js +6 -6
  106. package/src/components/icon/icon.js +1 -1
  107. package/src/components/icon/test/icon.test.js +66 -0
  108. package/src/components/input/Input.js +33 -39
  109. package/src/components/input/input.css +9 -6
  110. package/src/components/input/leu-input.js +5 -2
  111. package/src/components/input/stories/input.stories.js +8 -2
  112. package/src/components/input/test/input.test.js +431 -4
  113. package/src/components/menu/Menu.js +0 -5
  114. package/src/components/menu/MenuItem.js +22 -15
  115. package/src/components/menu/leu-menu-item.js +5 -2
  116. package/src/components/menu/leu-menu.js +5 -2
  117. package/src/components/menu/menu-item.css +5 -2
  118. package/src/components/menu/stories/menu-item.stories.js +13 -4
  119. package/src/components/menu/stories/menu.stories.js +11 -5
  120. package/src/components/menu/test/menu-item.test.js +180 -0
  121. package/src/components/menu/test/menu.test.js +10 -2
  122. package/src/components/pagination/Pagination.js +53 -65
  123. package/src/components/pagination/leu-pagination.js +5 -2
  124. package/src/components/pagination/stories/pagination.stories.js +17 -9
  125. package/src/components/pagination/test/pagination.test.js +191 -5
  126. package/src/components/popup/Popup.js +200 -0
  127. package/src/components/popup/leu-popup.js +6 -0
  128. package/src/components/popup/popup.css +27 -0
  129. package/src/components/popup/stories/popup.stories.js +58 -0
  130. package/src/components/popup/test/popup.test.js +29 -0
  131. package/src/components/radio/Radio.js +5 -10
  132. package/src/components/radio/RadioGroup.js +7 -39
  133. package/src/components/radio/leu-radio-group.js +5 -2
  134. package/src/components/radio/leu-radio.js +5 -2
  135. package/src/components/radio/radio-group.css +29 -0
  136. package/src/components/radio/stories/radio-group.stories.js +38 -19
  137. package/src/components/radio/stories/radio.stories.js +7 -1
  138. package/src/components/radio/test/radio-group.test.js +86 -0
  139. package/src/components/radio/test/radio.test.js +108 -17
  140. package/src/components/select/Select.js +35 -32
  141. package/src/components/select/leu-select.js +5 -2
  142. package/src/components/select/select.css +13 -13
  143. package/src/components/select/stories/select.stories.js +15 -168
  144. package/src/components/select/test/fixtures.js +162 -0
  145. package/src/components/select/test/select.test.js +236 -12
  146. package/src/components/table/Table.js +48 -123
  147. package/src/components/table/leu-table.js +5 -2
  148. package/src/components/table/stories/table.stories.js +20 -10
  149. package/src/components/table/table.css +99 -0
  150. package/src/components/table/test/table.test.js +1 -1
  151. package/src/lib/utils.js +17 -0
  152. package/{web-dev-server-storybook.config.mjs → web-dev-server.config.mjs} +1 -2
  153. package/web-test-runner.config.mjs +15 -2
@@ -1,5 +1,4 @@
1
1
  import { html, LitElement } from "lit"
2
- import { defineElement } from "../../lib/defineElement.js"
3
2
  import styles from "./[name].css"
4
3
 
5
4
  /**
@@ -27,7 +26,3 @@ export class Leu[Name] extends LitElement {
27
26
  `
28
27
  }
29
28
  }
30
-
31
- export function define[Name]Elements() {
32
- defineElement("[name]", [Namespace][Name])
33
- }
@@ -7,5 +7,5 @@
7
7
  --[name]-font-regular: var(--leu-font-regular);
8
8
  --[name]-font-black: var(--leu-font-black);
9
9
 
10
- font-family: var(--chip-font-regular);
10
+ font-family: var(--[name]-font-regular);
11
11
  }
@@ -1,3 +1,6 @@
1
- import { define[Name]Elements } from "./[Name].js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { [Namespace][Name] } from "./[Name].js"
2
3
 
3
- define[Name]Elements()
4
+ export { [Namespace][Name] }
5
+
6
+ defineElement("[name]", [Namespace][Name])
@@ -1,7 +1,6 @@
1
1
  import { LitElement, nothing } from "lit"
2
2
  import { html, unsafeStatic } from "lit/static-html.js"
3
3
 
4
- import { defineElement } from "../../lib/defineElement.js"
5
4
  import styles from "./accordion.css"
6
5
 
7
6
  /**
@@ -24,10 +23,10 @@ export class LeuAccordion extends LitElement {
24
23
  }
25
24
 
26
25
  static properties = {
27
- headingLevel: { type: Number, attribute: "heading-level" },
26
+ headingLevel: { type: Number, attribute: "heading-level", reflect: true },
28
27
  open: { type: Boolean, reflect: true },
29
- label: { type: String },
30
- labelPrefix: { type: String, attribute: "label-prefix" },
28
+ label: { type: String, reflect: true },
29
+ labelPrefix: { type: String, attribute: "label-prefix", reflect: true },
31
30
  }
32
31
 
33
32
  constructor() {
@@ -35,7 +34,6 @@ export class LeuAccordion extends LitElement {
35
34
  this.headingLevel = 2
36
35
  this.open = false
37
36
  this.label = ""
38
- this.labelPrefix = ""
39
37
  }
40
38
 
41
39
  /**
@@ -96,7 +94,3 @@ export class LeuAccordion extends LitElement {
96
94
  }
97
95
  /* eslint-enable lit/binding-positions, lit/no-invalid-html */
98
96
  }
99
-
100
- export function defineAccordionElements() {
101
- defineElement("accordion", LeuAccordion)
102
- }
@@ -1,3 +1,6 @@
1
- import { defineAccordionElements } from "./Accordion.js"
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuAccordion } from "./Accordion.js"
2
3
 
3
- defineAccordionElements()
4
+ export { LeuAccordion }
5
+
6
+ defineElement("accordion", LeuAccordion)
@@ -8,10 +8,7 @@ export default {
8
8
  component: "leu-accordion",
9
9
  argTypes: {
10
10
  headingLevel: {
11
- control: {
12
- type: "select",
13
- options: [0, 1, 2, 3, 4, 5, 6],
14
- },
11
+ options: [0, 1, 2, 3, 4, 5, 6],
15
12
  },
16
13
  content: {
17
14
  control: {
@@ -19,6 +16,12 @@ export default {
19
16
  },
20
17
  },
21
18
  },
19
+ parameters: {
20
+ design: {
21
+ type: "figma",
22
+ url: "https://www.figma.com/file/d6Pv21UVUbnBs3AdcZijHmbN/KTZH-Design-System?type=design&node-id=17341-82464&mode=design&t=lzVrtq8lxYVJU5TB-11",
23
+ },
24
+ },
22
25
  }
23
26
 
24
27
  function Template(args) {
@@ -4,12 +4,14 @@ import { fixture, expect } from "@open-wc/testing"
4
4
  import "../leu-accordion.js"
5
5
 
6
6
  async function defaultFixture() {
7
- return fixture(html` <leu-accordion /> `)
7
+ return fixture(
8
+ html` <leu-accordion label="Titel des Akkordeons"></leu-accordion> `
9
+ )
8
10
  }
9
11
 
10
12
  describe("LeuAccordion", () => {
11
13
  it("is a defined element", async () => {
12
- const el = await customElements.get("leu-accordion")
14
+ const el = customElements.get("leu-accordion")
13
15
 
14
16
  await expect(el).not.to.be.undefined
15
17
  })
@@ -19,4 +21,92 @@ describe("LeuAccordion", () => {
19
21
 
20
22
  await expect(el).shadowDom.to.be.accessible()
21
23
  })
24
+
25
+ it("renders the correct heading tag", async () => {
26
+ const el = await defaultFixture()
27
+
28
+ await expect(el._getHeadingTag()).to.equal("h2")
29
+
30
+ el.headingLevel = 1
31
+ await expect(el._getHeadingTag()).to.equal("h1")
32
+
33
+ el.headingLevel = 2
34
+ await expect(el._getHeadingTag()).to.equal("h2")
35
+
36
+ el.headingLevel = 3
37
+ await expect(el._getHeadingTag()).to.equal("h3")
38
+
39
+ el.headingLevel = 4
40
+ await expect(el._getHeadingTag()).to.equal("h4")
41
+
42
+ el.headingLevel = 5
43
+ await expect(el._getHeadingTag()).to.equal("h5")
44
+
45
+ el.headingLevel = 6
46
+ await expect(el._getHeadingTag()).to.equal("h6")
47
+ })
48
+
49
+ it("doesn't render an invalid heading tag", async () => {
50
+ const el = await defaultFixture()
51
+
52
+ el.headingLevel = 10
53
+ expect(el._getHeadingTag()).to.equal("h2")
54
+
55
+ el.headingLevel = -1
56
+ expect(el._getHeadingTag()).to.equal("h2")
57
+
58
+ el.headingLevel = 0
59
+ expect(el._getHeadingTag()).to.equal("h2")
60
+
61
+ el.headingLevel = "h3"
62
+ expect(el._getHeadingTag()).to.equal("h2")
63
+ })
64
+
65
+ it("toggles the open state", async () => {
66
+ const el = await fixture(
67
+ html`<leu-accordion label="Titel des Akkordeons"
68
+ ><div slot="content">Das ist der Inhalt</div></leu-accordion
69
+ >`
70
+ )
71
+
72
+ const button = el.shadowRoot.querySelector("button")
73
+
74
+ expect(el.open).to.be.false
75
+
76
+ button.click()
77
+ expect(el.open).to.be.true
78
+
79
+ button.click()
80
+ expect(el.open).to.be.false
81
+ })
82
+
83
+ it("doesn't show the content when it is closed", async () => {
84
+ const el = await fixture(
85
+ html`<leu-accordion label="Titel des Akkordeons"
86
+ ><div slot="content">Das ist der Inhalt</div></leu-accordion
87
+ >`
88
+ )
89
+
90
+ const button = el.shadowRoot.querySelector("button")
91
+ const content = el.shadowRoot.querySelector(".content")
92
+
93
+ expect(content).not.to.be.visible
94
+
95
+ button.click()
96
+ expect(content).to.be.visible
97
+ })
98
+
99
+ it("shows the label prefix before the label", async () => {
100
+ const el = await fixture(
101
+ html`<leu-accordion
102
+ label-prefix="01"
103
+ label="Titel des Akkordeons"
104
+ ></leu-accordion>`
105
+ )
106
+
107
+ const button = el.shadowRoot.querySelector("button")
108
+
109
+ expect(button).to.contain.text("01")
110
+ expect(button).to.contain.text("Titel des Akkordeons")
111
+ })
22
112
  })
@@ -0,0 +1,310 @@
1
+ import { html, LitElement, nothing } from "lit"
2
+ import { createRef, ref } from "lit/directives/ref.js"
3
+ import { classMap } from "lit/directives/class-map.js"
4
+
5
+ import styles from "./breadcrumb.css"
6
+ import { Icon } from "../icon/icon.js"
7
+ import "../menu/leu-menu.js"
8
+ import "../menu/leu-menu-item.js"
9
+ import "../popup/leu-popup.js"
10
+ import { debounce } from "../../lib/utils.js"
11
+
12
+ /**
13
+ * A Breadcrumb Navigation.
14
+ *
15
+ * The breadcrumbs can be displayed in two different layouts.
16
+ * Only the back link (the last item / parent of the current page)
17
+ * is displayed when…
18
+ * - … the width of the container is smaller
19
+ * than the BACK_ONLY_BREAKPOINT.
20
+ * - … less then two breadcrumb items could be displayed
21
+ * without overflowing the container.
22
+ *
23
+ * Otherwise as many items as possible are displayed in an inline list
24
+ * without overflowing the container. The remaining items are displayed
25
+ * in a dropdown menu.
26
+ *
27
+ * In order to determine the exact numbers of items that have to be
28
+ * hidden inside the dropdown, all of them have to be rendered first.
29
+ * 1. Render all items
30
+ * 2. Calculate (measure) the number of items that can be displayed
31
+ * without overflowing the container.
32
+ * 3. Updating the state (_hiddeItems) which will trigger a rerender
33
+ * 4. Render the items again with the new state.
34
+ *
35
+ * This results in multiple updates scheduled one after another. Lit
36
+ * will also print a waring in the console beacause of that.
37
+ * It's no a nice behaviour but the only one that works without
38
+ * having duplicate and hidden markup to derive the sizes from that.
39
+ *
40
+ *
41
+ * @prop {Array} items - Object array with { label, href }
42
+ * @prop {Boolean} inverted - invert color on dark background
43
+ *
44
+ * @tagname leu-breadcrumb
45
+ */
46
+ export class LeuBreadcrumb extends LitElement {
47
+ static styles = styles
48
+
49
+ static properties = {
50
+ items: { type: Array },
51
+ inverted: { type: Boolean, reflect: true },
52
+
53
+ _hiddenItems: { state: true },
54
+ _showBackOnly: { state: true },
55
+ _isRecalculating: { state: true },
56
+ _isDropdownOpen: { state: true },
57
+ }
58
+
59
+ static BACK_ONLY_BREAKPOINT = 320
60
+
61
+ constructor() {
62
+ super()
63
+ /** @type {Array} */
64
+ this.items = []
65
+ /** @type {Boolean} - will be used on dark Background */
66
+ this.inverted = false
67
+
68
+ /** @internal */
69
+ this._containerRef = createRef()
70
+ /** @internal */
71
+ this._hiddenItems = 0
72
+ /** @internal */
73
+ this._showBackOnly = null
74
+ /** @internal */
75
+ this._lastContainerWidth = null
76
+ /**
77
+ * @internal
78
+ * Forces the toggle button to be rendered
79
+ * so that all possible inline items will be measured.
80
+ * */
81
+ this._isRecalculating = true
82
+ /** @internal */
83
+ this._isDropdownOpen = false
84
+
85
+ this.resizeObserver = new ResizeObserver(
86
+ debounce(() => {
87
+ this._handleResize()
88
+ }, 500)
89
+ )
90
+ }
91
+
92
+ firstUpdated() {
93
+ this.resizeObserver.observe(this._containerRef.value)
94
+ }
95
+
96
+ async updated(changedProperties) {
97
+ if (changedProperties.has("items")) {
98
+ this._hiddenItems = 0
99
+ this._isRecalculating = true
100
+ await this.updateComplete
101
+ this._checkWidth()
102
+ }
103
+ }
104
+
105
+ disconnectedCallback() {
106
+ super.disconnectedCallback()
107
+
108
+ window.removeEventListener("click", this._closeDropdown)
109
+ this.resizeObserver.disconnect()
110
+ }
111
+
112
+ /** @internal */
113
+ get _listItems() {
114
+ return this.items.toSpliced(1, this._hiddenItems)
115
+ }
116
+
117
+ /** @internal */
118
+ get _dropdownItems() {
119
+ return this.items.slice(1, 1 + this._hiddenItems)
120
+ }
121
+
122
+ _handleResize = async () => {
123
+ const containerOffsetWidth = this._containerRef.value.offsetWidth
124
+ const sizeIsGrowing = containerOffsetWidth > this._lastContainerWidth
125
+ this._lastContainerWidth = containerOffsetWidth
126
+
127
+ /**
128
+ * Show only the back link (parent of the current page)
129
+ * when the width of the container is smaller than the BACK_ONLY_BREAKPOINT
130
+ */
131
+ if (containerOffsetWidth <= LeuBreadcrumb.BACK_ONLY_BREAKPOINT) {
132
+ this._showBackOnly = true
133
+ this._isRecalculating = false
134
+ return
135
+ }
136
+
137
+ this._showBackOnly = false
138
+
139
+ /**
140
+ * In order to calculate how many items can be displayed
141
+ * when the container is growing, all items have to
142
+ * be marked as displayed (_hiddenItems = 0) and
143
+ * rendered.
144
+ */
145
+ if (sizeIsGrowing && this._hiddenItems > 0) {
146
+ this._hiddenItems = 0
147
+ this._isRecalculating = true
148
+ await this.updateComplete
149
+ }
150
+
151
+ this._checkWidth()
152
+ }
153
+
154
+ /**
155
+ * Calculate the number of items that can be displayed
156
+ * without overflowing the container.
157
+ * @internal
158
+ * @returns {void}
159
+ */
160
+ _checkWidth() {
161
+ const containerOffsetWidth = this._containerRef.value.offsetWidth
162
+ const containerScrollWidth = this._containerRef.value.scrollWidth
163
+ this._lastContainerWidth = containerOffsetWidth
164
+
165
+ /** When the container is not overflowing, nothing has to be done */
166
+ if (containerOffsetWidth === containerScrollWidth) {
167
+ this._isRecalculating = false
168
+ return
169
+ }
170
+
171
+ const listItems = this._containerRef.value.querySelectorAll(
172
+ "li:not([data-dropdown-toggle])"
173
+ )
174
+ const listItemWidths = [...listItems].map((o) => o.offsetWidth)
175
+
176
+ let hiddenItems = 0
177
+ let hiddenItemsWidth = 0
178
+
179
+ /**
180
+ * Remove item by item until the sum of the remaining items
181
+ * is smaller than the width of the container.
182
+ * The first item will not be removed.
183
+ */
184
+ while (
185
+ hiddenItems < listItemWidths.length &&
186
+ containerOffsetWidth < containerScrollWidth - hiddenItemsWidth
187
+ ) {
188
+ hiddenItems += 1
189
+
190
+ hiddenItemsWidth = listItemWidths
191
+ .slice(1, 1 + hiddenItems)
192
+ .reduce((sum, itemWidth) => sum + itemWidth, 0)
193
+ }
194
+
195
+ this._hiddenItems += hiddenItems
196
+ this._isRecalculating = false
197
+ }
198
+
199
+ /** @internal */
200
+ _handleDropdownToggle = (e) => {
201
+ e.stopPropagation()
202
+
203
+ this._isDropdownOpen = !this._isDropdownOpen
204
+
205
+ if (this._isDropdownOpen) {
206
+ window.addEventListener("click", this._closeDropdown)
207
+ } else {
208
+ window.removeEventListener("click", this._closeDropdown)
209
+ }
210
+ }
211
+
212
+ _closeDropdown = () => {
213
+ this._isDropdownOpen = false
214
+ window.removeEventListener("click", this._closeDropdown)
215
+ }
216
+
217
+ /**
218
+ * Render the dropdown menu
219
+ * @returns
220
+ */
221
+ renderDropdown() {
222
+ if (this._dropdownItems.length === 0 && !this._isRecalculating)
223
+ return nothing
224
+
225
+ return html`
226
+ <li class="breadcrumbs__item" data-dropdown-toggle>
227
+ <span class="breadcrumbs__icon">${Icon("angleRight")}</span>
228
+ <leu-popup
229
+ ?active=${this._isDropdownOpen}
230
+ placement="bottom-start"
231
+ shift
232
+ shiftPadding="8"
233
+ autoSize="width"
234
+ autoSizePadding="8"
235
+ >
236
+ <button
237
+ slot="anchor"
238
+ class="menu"
239
+ @click=${this._handleDropdownToggle}
240
+ tabindex="0"
241
+ >
242
+ &hellip;
243
+ </button>
244
+ <div class="dropdown">
245
+ ${html`
246
+ <leu-menu>
247
+ ${this._dropdownItems.map(
248
+ (item) =>
249
+ html`
250
+ <leu-menu-item
251
+ label=${item.label}
252
+ href=${item.href}
253
+ ></leu-menu-item>
254
+ `
255
+ )}
256
+ </leu-menu>
257
+ `}
258
+ </div>
259
+ </leu-popup>
260
+ </li>
261
+ `
262
+ }
263
+
264
+ render() {
265
+ if (this.items.length < 2) return nothing
266
+
267
+ const parentItem = this.items[this.items.length - 2]
268
+
269
+ const showBackOnly =
270
+ this._showBackOnly || this.items.length - this._hiddenItems < 2
271
+
272
+ const wrapperClasses = {
273
+ breadcrumbs: true,
274
+ "breadcrumbs--back-only": showBackOnly,
275
+ }
276
+
277
+ return html`
278
+ <nav class=${classMap(wrapperClasses)}>
279
+ <h2 class="visuallyhidden">Sie sind hier:</h2>
280
+ <ol class="breadcrumbs__list" ref=${ref(this._containerRef)}>
281
+ ${showBackOnly
282
+ ? html` <li class="breadcrumbs__item breadcrumbs__item--back">
283
+ <span class="breadcrumbs__icon">${Icon("arrowLeft")}</span>
284
+ <a class="breadcrumbs__link" href=${parentItem.href}
285
+ >${parentItem.label}</a
286
+ >
287
+ </li>`
288
+ : this._listItems.map(
289
+ (item, index, list) =>
290
+ html`
291
+ <li class="breadcrumbs__item">
292
+ ${index > 0
293
+ ? html`<span class="breadcrumbs__icon"
294
+ >${Icon("angleRight")}</span
295
+ >` // First list item doesn't have an arrow
296
+ : nothing}
297
+ ${index === list.length - 1
298
+ ? item.label // Last list item doesn't contain a link
299
+ : html`<a class="breadcrumbs__link" href=${item.href}
300
+ >${item.label}</a
301
+ >`}
302
+ </li>
303
+ ${index === 0 ? this.renderDropdown() : nothing}
304
+ `
305
+ )}
306
+ </ol>
307
+ </nav>
308
+ `
309
+ }
310
+ }
@@ -0,0 +1,114 @@
1
+ :host,
2
+ :host * {
3
+ box-sizing: border-box;
4
+ }
5
+
6
+ :host {
7
+ --breadcrumb-font-regular: var(--leu-font-regular);
8
+ --breadcrumb-font-black: var(--leu-font-black);
9
+
10
+ font-family: var(--breadcrumb-font-regular);
11
+ line-height: 1.5;
12
+ color: var(--leu-color-black-100);
13
+ }
14
+
15
+ :host([inverted]) {
16
+ color: var(--leu-color-black-0);
17
+ }
18
+
19
+ .breadcrumbs__list {
20
+ display: flex;
21
+ align-items: center;
22
+ list-style-type: none;
23
+ margin: 0;
24
+ padding: 0;
25
+ }
26
+
27
+ .breadcrumbs--back-only .breadcrumbs__list {
28
+ overflow: hidden;
29
+ }
30
+
31
+ .breadcrumbs__item,
32
+ .breadcrumbs__icon {
33
+ align-items: center;
34
+ display: flex;
35
+ min-height: 1.875rem;
36
+ white-space: nowrap;
37
+ }
38
+
39
+ .breadcrumbs__item:first-child:not(.breadcrumbs__item--back) {
40
+ font-family: var(--breadcrumb-font-black);
41
+ }
42
+
43
+ .breadcrumbs__item--back {
44
+ max-width: 100%;
45
+ }
46
+
47
+ .breadcrumbs__link {
48
+ color: inherit;
49
+ text-decoration: none;
50
+ transition: color 0.1s ease;
51
+ white-space: nowrap;
52
+ }
53
+
54
+ .breadcrumbs__item--back .breadcrumbs__link {
55
+ overflow: hidden;
56
+ text-overflow: ellipsis;
57
+ margin-right: 0.25rem;
58
+ }
59
+
60
+ .menu {
61
+ background: none;
62
+ color: inherit;
63
+ cursor: pointer;
64
+ border: 2px solid transparent;
65
+ }
66
+
67
+ .menu:focus-visible {
68
+ outline: 2px solid var(--leu-color-func-cyan);
69
+ outline-offset: 2px;
70
+ }
71
+
72
+ .dropdown {
73
+ background-color: var(--leu-color-black-0);
74
+ box-shadow: var(--leu-box-shadow-short);
75
+ }
76
+
77
+ .visuallyhidden {
78
+ clip: rect(0 0 0 0);
79
+ border: 0;
80
+ height: 1px;
81
+ margin: -1px !important;
82
+ overflow: hidden;
83
+ padding: 0 !important;
84
+ position: absolute;
85
+ width: 1px;
86
+ }
87
+
88
+ .breadcrumbs {
89
+ font-size: 1rem;
90
+ }
91
+
92
+ @media (width >= 320px) {
93
+ .breadcrumbs {
94
+ font-size: calc(2.5vw + 8px);
95
+ }
96
+ }
97
+
98
+ @media (width >= 400px) {
99
+ .breadcrumbs {
100
+ font-size: 18px;
101
+ }
102
+ }
103
+
104
+ @media (width >= 1024px) {
105
+ .breadcrumbs {
106
+ font-size: calc(0.7813vw + 10px);
107
+ }
108
+ }
109
+
110
+ @media (width >= 1280px) {
111
+ .breadcrumbs {
112
+ font-size: 20px;
113
+ }
114
+ }
@@ -0,0 +1,6 @@
1
+ import { defineElement } from "../../lib/defineElement.js"
2
+ import { LeuBreadcrumb } from "./Breadcrumb.js"
3
+
4
+ export { LeuBreadcrumb }
5
+
6
+ defineElement("breadcrumb", LeuBreadcrumb)