u2ui 1.5.20 → 1.7.5

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.
@@ -19,6 +19,26 @@ Disable form-elements (like inputs, buttons, fieldsets) based on a condition
19
19
  }
20
20
  ```
21
21
 
22
+ ## Conditions
23
+
24
+ | condition | disabled while |
25
+ |---|---|
26
+ | `name` | a value of `name` is not empty |
27
+ | `!name` | no value of `name` is not empty |
28
+ | `name=v` | a value equals `v` |
29
+ | `name!=v` | no value equals `v` |
30
+ | `name>n`, `name<n` | a value is a number greater / less than `n` |
31
+
32
+ A field's values are what the form would send under its name (`FormData`): none for an unchecked
33
+ checkbox or a disabled field, several for same-named fields or a `select multiple`, a file by its name.
34
+ So a field disabled by its own condition counts as empty for the conditions that depend on it, and
35
+ chains settle in one go. Each check builds the `FormData`, which fires the form's `formdata` event.
36
+ A field that is not in the form counts as empty, with a warning (a typo?).
37
+
38
+ Typing applies at once; fields and conditions added, removed or changed in the form apply on the
39
+ next microtask. A value set by script (`input.value = …`) is only seen with an `input` event.
40
+ Removing the attribute leaves the element as it is.
41
+
22
42
  ## Install
23
43
 
24
44
  ```html
@@ -29,6 +49,7 @@ Disable form-elements (like inputs, buttons, fieldsets) based on a condition
29
49
 
30
50
  [test.html](http://gcdn.li/u2ui/u2@main/attr/disableif/tests/test.html)
31
51
  [minimal.html](http://gcdn.li/u2ui/u2@main/attr/disableif/tests/minimal.html)
52
+ [disableif.test.html](http://gcdn.li/u2ui/u2@main/attr/disableif/tests/disableif.test.html) (tests)
32
53
 
33
54
  ## About
34
55
 
@@ -1,59 +1,86 @@
1
1
  import { SelectorObserver } from '../../js/SelectorObserver/SelectorObserver.js';
2
2
 
3
- function checkCondition(el) {
4
- const condition = el.getAttribute('u2-disableif');
5
-
6
- const match = condition.match(/^(!?)([^=!<>]*)(=|!=|>|<)?([^=!<>]*)$/);
7
- if (match) {
8
- let [_, not, field, operator, value] = match;
9
- if (not) {
10
- if (operator) console.warn(`u2-disableif: "!" operator is not supported with other operators`, { el, field, operator, value });
11
- operator = '=';
12
- }
13
- const target = el.form.elements[field];
14
- if (!target) {
15
- console.warn(`u2-disableif: field "${field}" not found`, { el, field, operator, value });
16
- return;
17
- }
18
- let stringTargetValue = target.value;
19
- if (target.type === 'checkbox') stringTargetValue = target.checked ? target.value : '';
3
+ /** Once per element and problem, not on every update. */
4
+ const warned = new WeakMap();
5
+ function warn(el, problem) {
6
+ if (warned.get(el) === problem) return;
7
+ warned.set(el, problem);
8
+ console.warn('u2-disableif: ' + problem, el);
9
+ }
20
10
 
21
- const numericTargetValue = parseFloat(stringTargetValue);
22
- const numericConditionValue = parseFloat(value);
11
+ /**
12
+ * Whether the element's condition holds. A field's values are what the form would send under its
13
+ * name (FormData): none for an unchecked box or a disabled field, several for same-named fields or
14
+ * a select multiple; a file counts by its name. undefined: no valid condition (yet).
15
+ */
16
+ function holds(el, data) {
17
+ const match = el.getAttribute('u2-disableif').match(/^(!?)([^=!<>]*)(=|!=|>|<)?([^=!<>]*)$/);
18
+ if (!match) return;
19
+ const [, not, field, operator, value] = match;
20
+ const form = el.form;
21
+ // a custom element that is not defined yet is no form field yet: check again once it is
22
+ const pending = !form.elements[field] && form.querySelector(`:not(:defined)[name="${CSS.escape(field)}"]`);
23
+ if (pending) { customElements.whenDefined(pending.localName).then(() => schedule(form)); return; }
24
+ if (not && operator) { warn(el, `"!" cannot be combined with "${operator}"`); return; }
25
+ if (!form.elements[field]) warn(el, `field "${field}" not found, so it is empty`); // a typo?
26
+ const values = data.getAll(field).map(v => typeof v === 'string' ? v : v.name);
27
+ switch (operator) {
28
+ case '=': return values.includes(value);
29
+ case '!=': return !values.includes(value);
30
+ case '>': return values.some(v => parseFloat(v) > parseFloat(value));
31
+ case '<': return values.some(v => parseFloat(v) < parseFloat(value));
32
+ }
33
+ return values.some(Boolean) !== !!not;
34
+ }
23
35
 
24
- let conditionMet = false;
25
- switch (operator) {
26
- case undefined:
27
- conditionMet = !!stringTargetValue;
28
- break;
29
- case '=':
30
- conditionMet = stringTargetValue === value;
31
- break;
32
- case '!=':
33
- conditionMet = stringTargetValue !== value;
34
- break;
35
- case '>':
36
- conditionMet = numericTargetValue > numericConditionValue;
37
- break;
38
- case '<':
39
- conditionMet = numericTargetValue < numericConditionValue;
40
- break;
36
+ /** Applies all conditions of the form, again until nothing changes: disabling a field empties it for
37
+ * the conditions that depend on it. A cycle stops after as many passes as there are conditions. */
38
+ function update(form) {
39
+ const els = [...form.elements].filter(el => el.hasAttribute('u2-disableif'));
40
+ for (let pass = 0; pass <= els.length; pass++) {
41
+ const data = new FormData(form); // fires "formdata", once per pass
42
+ let changed = false;
43
+ for (const el of els) {
44
+ const disabled = holds(el, data);
45
+ if (disabled === undefined || disabled === el.disabled) continue;
46
+ el.disabled = disabled;
47
+ changed = true;
41
48
  }
42
- el.disabled = conditionMet;
49
+ if (!changed) return;
43
50
  }
44
51
  }
45
52
 
53
+ /** One update per form and `wait` (microtask, frame), however many changes ask for it. */
54
+ const scheduled = new Map();
55
+ function schedule(form, wait = queueMicrotask) {
56
+ const forms = scheduled.get(wait) ?? scheduled.set(wait, new WeakSet()).get(wait);
57
+ if (!form || forms.has(form)) return;
58
+ forms.add(form);
59
+ wait(() => { forms.delete(form); update(form); });
60
+ }
61
+
62
+ /** Typing applies at once. Fields and conditions that are added, removed or changed in the form
63
+ * apply on the next microtask; one that changes its form also updates the new one. */
64
+ const ATTRIBUTES = ['u2-disableif', 'name', 'form', 'value', 'checked', 'selected', 'multiple', 'type'];
65
+ const watched = new WeakSet();
66
+ function watch(form) {
67
+ if (!form || watched.has(form)) return;
68
+ watched.add(form);
69
+ form.addEventListener('input', () => update(form));
70
+ form.addEventListener('reset', () => setTimeout(() => update(form))); // the values change after the event
71
+ new MutationObserver(records => {
72
+ schedule(form);
73
+ for (const { target } of records) if (target.form !== form) { watch(target.form); schedule(target.form); }
74
+ }).observe(form, { subtree: true, childList: true, attributes: true, attributeFilter: ATTRIBUTES });
75
+ }
46
76
 
47
77
  new SelectorObserver({
48
78
  on: (el) => {
49
- checkCondition(el);
79
+ watch(el.form);
80
+ schedule(el.form);
50
81
  // wait for custom-element-inputs
51
- // queueMicrotask(()=>checkCondition(el));
52
- requestAnimationFrame(()=>checkCondition(el));
53
- //setTimeout(()=>checkCondition(el));
54
- const form = el.form;
55
- el.form.addEventListener('input', () => checkCondition(el));
82
+ // queueMicrotask(() => schedule(el.form));
83
+ schedule(el.form, requestAnimationFrame);
84
+ // setTimeout(() => schedule(el.form));
56
85
  },
57
- off: (el) => {
58
- }
59
86
  }).observe('[u2-disableif]');
@@ -1,13 +1,13 @@
1
1
  # <u2-calendar> - element (BETA)
2
- Calendar / month view component, use `<u2-calendaritem>` as children
2
+ Calendar / month view component, use `<u2-calendar-item>` as children
3
3
 
4
4
  > Status: BETA — experimental. API and markup may change.
5
5
 
6
6
  ## Features
7
7
 
8
- - month view
8
+ - month view, always six weeks: every month has the same height
9
9
  - lightweight layout and styling
10
- - supports `u2-calendaritem` children with `start` and `end` attributes
10
+ - supports `u2-calendar-item` children with `start` and `end` attributes
11
11
  - events rendered as colored bars spanning multiple days
12
12
  - performat and responsive using css grid
13
13
 
@@ -21,8 +21,8 @@ u2Calendaritem2.addEventListener('click',()=>{
21
21
 
22
22
  ```html
23
23
  <u2-calendar date="2025-10-01">
24
- <u2-calendaritem start="2025-10-09">Demo</u2-calendaritem>
25
- <u2-calendaritem start="2025-10-19" end="2025-10-23" id="u2Calendaritem2">Demo 2</u2-calendaritem>
24
+ <u2-calendar-item start="2025-10-09">Demo</u2-calendar-item>
25
+ <u2-calendar-item start="2025-10-19" end="2025-10-23" id="u2Calendaritem2">Demo 2</u2-calendar-item>
26
26
  </u2-calendar>
27
27
 
28
28
  ```
@@ -31,7 +31,7 @@ u2Calendaritem2.addEventListener('click',()=>{
31
31
  u2-calendar {
32
32
  font-size:10px;
33
33
  }
34
- u2-calendaritem {
34
+ u2-calendar-item {
35
35
  background-color:var(--color, orange);
36
36
  &:hover {
37
37
  background-color:var(--color-dark, red);
@@ -6,7 +6,7 @@ u2-calendar {
6
6
  z-index: 1;
7
7
  }
8
8
  }
9
- u2-calendaritem {
9
+ u2-calendar-item {
10
10
  &[draggable="true"] {
11
11
  cursor: move;
12
12
  }
@@ -20,7 +20,7 @@ u2-calendaritem {
20
20
  u2-calendar:not(:defined) {
21
21
  display:block;
22
22
  }
23
- u2-calendaritem:not(:defined) {
23
+ u2-calendar-item:not(:defined) {
24
24
  display:block;
25
25
  padding:.2em .9em;
26
26
  margin:.2em 0;
@@ -62,19 +62,25 @@ export default class U2Calendar extends U2Element {
62
62
  </style>
63
63
  <div class=navigation>
64
64
  <h2></h2>
65
- <button onclick="this.getRootNode().host.date = new Date()" style="padding-inline:.5rem">
65
+ <button name=today style="padding-inline:.5rem">
66
66
  Today
67
67
  </button>
68
- <button onclick="this.getRootNode().host.prev()">
68
+ <button name=prev>
69
69
  <u2-ico icon="chevron-left">&lt;</u2-ico>
70
70
  </button>
71
- <button onclick="this.getRootNode().host.next()">
71
+ <button name=next>
72
72
  <u2-ico icon="chevron-right">&gt;</u2-ico>
73
73
  </button>
74
74
  </div>
75
75
  <div class=header></div>
76
76
  <div class=grid role=grid><slot></slot></div>
77
77
  `;
78
+ // no inline handlers: a CSP without 'unsafe-inline' would block them
79
+ this.shadowRoot.querySelector('.navigation').addEventListener('click', (e) => {
80
+ const name = e.target.closest('button')?.name;
81
+ if (name === 'today') this.date = new Date();
82
+ else if (name) this[name]();
83
+ });
78
84
  }
79
85
 
80
86
  _setLocale(localeStr) {
@@ -154,10 +160,9 @@ export default class U2Calendar extends U2Element {
154
160
  const firstOfMonth = new Date(Date.UTC(year, month, 1));
155
161
  const startOffset = (firstOfMonth.getUTCDay() - this._weekStart + 7) % 7;
156
162
 
157
- for (let i = 0; i < 42; i++) { // 6 Wochen x 7 Tage
163
+ for (let i = 0; i < 42; i++) { // always 6 weeks, so every month has the same height
158
164
  const date = new Date(Date.UTC(year, month, 1 - startOffset + i));
159
165
  const isOtherMonth = date.getUTCMonth() !== month;
160
- if (isOtherMonth && i === 35) break; // stop, if other month and last row
161
166
  const isToday = date.toDateString() === today.toDateString();
162
167
  grid.appendChild(this.createDayCell(date, i, isOtherMonth, isToday));
163
168
  }
@@ -190,7 +195,7 @@ export default class U2Calendar extends U2Element {
190
195
  });
191
196
  }
192
197
  _updateLayout() {
193
- const items = Array.from(this.querySelectorAll('u2-calendaritem'));
198
+ const items = Array.from(this.querySelectorAll('u2-calendar-item'));
194
199
 
195
200
  // Berechne Wochen-Segmente für alle Items
196
201
  const itemWeeks = items.map(item => ({
@@ -235,14 +240,11 @@ export default class U2Calendar extends U2Element {
235
240
  splitIntoWeeks(start, end, year, month) {
236
241
  const weeks = [];
237
242
  const firstDay = new Date(Date.UTC(year, month, 1));
238
- const monthEnd = new Date(Date.UTC(year, month + 1, 0));
239
243
  const startOffset = (firstDay.getUTCDay() - this._weekStart + 7) % 7;
240
244
 
241
245
  // Calculate the first and last day of the visible calendar view
242
246
  const viewStart = new Date(Date.UTC(year, month, 1 - startOffset));
243
- const daysInMonth = monthEnd.getUTCDate();
244
- const totalDays = Math.ceil((daysInMonth + startOffset) / 7) * 7;
245
- const viewEnd = new Date(Date.UTC(year, month, 1 - startOffset + totalDays - 1));
247
+ const viewEnd = new Date(Date.UTC(year, month, 1 - startOffset + 41)); // 6 weeks, as rendered
246
248
 
247
249
  // Parse input dates and normalize to start of day
248
250
  const eventStart = new Date(start);
@@ -387,4 +389,6 @@ function getTimeFormatter(localeTag, options = { hour: '2-digit', minute: '2-dig
387
389
 
388
390
 
389
391
  customElements.get('u2-calendar') || customElements.define('u2-calendar', U2Calendar);
390
- customElements.get('u2-calendaritem') || customElements.define('u2-calendaritem', U2CalendarItem);
392
+ customElements.get('u2-calendar-item') || customElements.define('u2-calendar-item', U2CalendarItem);
393
+
394
+ export const extraElements = { 'u2-calendar-item': U2CalendarItem };
@@ -17,7 +17,7 @@ function removePart(el, partName) {
17
17
  document.addEventListener('dragstart', (e) => {
18
18
  const item = e.target.closest('[draggable="true"]');
19
19
  if (!item) return;
20
- if (item.tagName.toLowerCase() !== 'u2-calendaritem') return;
20
+ if (item.tagName.toLowerCase() !== 'u2-calendar-item') return;
21
21
  draggedItem = item;
22
22
  e.dataTransfer.effectAllowed = 'move';
23
23
  e.dataTransfer.setData('text/plain', '');
@@ -76,7 +76,7 @@ document.addEventListener('wheel', e => {
76
76
 
77
77
  // move event by one day left or right
78
78
  document.addEventListener('keydown', e => {
79
- const item = e.target.closest('u2-calendaritem');
79
+ const item = e.target.closest('u2-calendar-item');
80
80
  if (!item) return;
81
81
  if (!item.draggable) return;
82
82
 
@@ -47,7 +47,7 @@
47
47
  "cssProperties": [],
48
48
  "cssParts": [],
49
49
  "superclass": { "name": "HTMLElement" },
50
- "tagName": "u2-calendaritem",
50
+ "tagName": "u2-calendar-item",
51
51
  "customElement": true
52
52
  }],
53
53
  "exports": [{
@@ -59,7 +59,7 @@
59
59
  }
60
60
  },{
61
61
  "kind": "custom-element-definition",
62
- "name": "u2-calendaritem",
62
+ "name": "u2-calendar-item",
63
63
  "declaration": {
64
64
  "name": "U2CalendarItem",
65
65
  "module": "calendar.js"
@@ -1,10 +1,10 @@
1
- # &lt;u2-carousel-nav&gt; - element
1
+ # &lt;u2-carouselnav&gt; - element
2
2
  Carousel navigation element for u2-carousel. (beta)
3
3
 
4
4
  ## Usage
5
5
 
6
6
  ```html
7
- <u2-carousel-nav></u2-carousel-nav>
7
+ <u2-carouselnav></u2-carouselnav>
8
8
  <u2-carousel>
9
9
  <div>Item 1</div>
10
10
  <div>Item 2</div>
@@ -13,10 +13,10 @@ Carousel navigation element for u2-carousel. (beta)
13
13
  ```
14
14
 
15
15
  ```css
16
- u2-carousel-nav > * {
16
+ u2-carouselnav > * {
17
17
  transition:.3s;
18
18
  }
19
- u2-carousel-nav > .-active {
19
+ u2-carouselnav > .-active {
20
20
  transform:scale(1.2);
21
21
  }
22
22
  ```
@@ -24,13 +24,13 @@ u2-carousel-nav > .-active {
24
24
  ## Install
25
25
 
26
26
  ```html
27
- <script src="https://cdn.jsdelivr.net/gh/u2ui/u2@main/el/carousel-nav/carousel-nav.js" type=module async></script>
27
+ <script src="https://cdn.jsdelivr.net/gh/u2ui/u2@main/el/carouselnav/carouselnav.js" type=module async></script>
28
28
  ```
29
29
 
30
30
  ## Demos
31
31
 
32
- [test.html](http://gcdn.li/u2ui/u2@main/el/carousel-nav/tests/test.html)
33
- [minimal.html](http://gcdn.li/u2ui/u2@main/el/carousel-nav/tests/minimal.html)
32
+ [test.html](http://gcdn.li/u2ui/u2@main/el/carouselnav/tests/test.html)
33
+ [minimal.html](http://gcdn.li/u2ui/u2@main/el/carouselnav/tests/minimal.html)
34
34
 
35
35
  ## About
36
36
 
@@ -1,13 +1,13 @@
1
1
 
2
2
  const style = document.createElement('style');
3
3
  style.innerHTML = `
4
- u2-carousel-nav {
4
+ u2-carouselnav {
5
5
  display:flex;
6
6
  justify-content:center;
7
7
  margin:.5rem;
8
8
  gap:.5rem;
9
9
  }
10
- u2-carousel-nav > * {
10
+ u2-carouselnav > * {
11
11
  width:1.2rem;
12
12
  height:1.2rem;
13
13
  border-radius:99rem;
@@ -22,10 +22,10 @@ u2-carousel-nav > * {
22
22
  align-items:center;
23
23
  opacity:.3;
24
24
  }
25
- u2-carousel-nav > :hover {
25
+ u2-carouselnav > :hover {
26
26
  opacity:.6;
27
27
  }
28
- u2-carousel-nav > .-active {
28
+ u2-carouselnav > .-active {
29
29
  opacity:1;
30
30
  }
31
31
  `
@@ -71,4 +71,4 @@ export default class U2CarouselNav extends HTMLElement {
71
71
  })
72
72
  }
73
73
  }
74
- customElements.get('u2-carousel-nav') || customElements.define('u2-carousel-nav', U2CarouselNav)
74
+ customElements.get('u2-carouselnav') || customElements.define('u2-carouselnav', U2CarouselNav)
@@ -3,7 +3,7 @@
3
3
  "readme": "README.md",
4
4
  "modules": [{
5
5
  "kind": "javascript-module",
6
- "path": "carousel-nav.js",
6
+ "path": "carouselnav.js",
7
7
  "declarations": [{
8
8
  "kind": "class",
9
9
  "description": "Carousel navigation element for u2-carousel. (beta)",
@@ -16,15 +16,15 @@
16
16
  }],
17
17
  "cssProperties": [],
18
18
  "superclass": { "name": "HTMLElement" },
19
- "tagName": "u2-carousel-nav",
19
+ "tagName": "u2-carouselnav",
20
20
  "customElement": true
21
21
  }],
22
22
  "exports": [{
23
23
  "kind": "custom-element-definition",
24
- "name": "u2-carousel-nav",
24
+ "name": "u2-carouselnav",
25
25
  "declaration": {
26
26
  "name": "U2CarouselNav",
27
- "module": "carousel-nav.js"
27
+ "module": "carouselnav.js"
28
28
  }
29
29
  }]
30
30
  }]
@@ -1,4 +1,4 @@
1
- # &lt;u2-parallax-bg&gt; - element
1
+ # &lt;u2-parallaxbg&gt; - element
2
2
  Scroll-driven parallax background-layer with dynamic container sizing for full content visibility
3
3
 
4
4
  ## Features
@@ -13,18 +13,18 @@ Scroll-driven parallax background-layer with dynamic container sizing for full c
13
13
  ## Usage
14
14
 
15
15
  ```html
16
- <div class=u2-parallax-bg-stage style="height:100%">
17
- <u2-parallax-bg>
18
- <img src="https://raw.githubusercontent.com/u2ui/u2/main/el/parallax-bg/tests/bg.jpg" alt="parallax-bg demo">
19
- </u2-parallax-bg>
16
+ <div class=u2-parallaxbg-stage style="height:100%">
17
+ <u2-parallaxbg>
18
+ <img src="https://raw.githubusercontent.com/u2ui/u2/main/el/parallaxbg/tests/bg.jpg" alt="parallaxbg demo">
19
+ </u2-parallaxbg>
20
20
  </div>
21
21
  ```
22
22
 
23
23
  ```css
24
- u2-parallax-bg {
25
- --parallax-bg-speed:.7
24
+ u2-parallaxbg {
25
+ --parallaxbg-speed:.7
26
26
  }
27
- u2-parallax-bg div {
27
+ u2-parallaxbg div {
28
28
  display:grid;
29
29
  place-items: center;
30
30
  }
@@ -33,16 +33,16 @@ u2-parallax-bg div {
33
33
  ## Install
34
34
 
35
35
  ```html
36
- <link href="https://cdn.jsdelivr.net/gh/u2ui/u2@main/el/parallax-bg/parallax-bg.css" rel=stylesheet>
37
- <script src="https://cdn.jsdelivr.net/gh/u2ui/u2@main/el/parallax-bg/parallax-bg.js" type=module async></script>
36
+ <link href="https://cdn.jsdelivr.net/gh/u2ui/u2@main/el/parallaxbg/parallaxbg.css" rel=stylesheet>
37
+ <script src="https://cdn.jsdelivr.net/gh/u2ui/u2@main/el/parallaxbg/parallaxbg.js" type=module async></script>
38
38
  ```
39
39
 
40
40
  ## Demos
41
41
 
42
- [test.html](http://gcdn.li/u2ui/u2@main/el/parallax-bg/tests/test.html)
43
- [visible.html](http://gcdn.li/u2ui/u2@main/el/parallax-bg/tests/visible.html)
44
- [minimal.html](http://gcdn.li/u2ui/u2@main/el/parallax-bg/tests/minimal.html)
45
- [demo.html](http://gcdn.li/u2ui/u2@main/el/parallax-bg/tests/demo.html)
42
+ [test.html](http://gcdn.li/u2ui/u2@main/el/parallaxbg/tests/test.html)
43
+ [visible.html](http://gcdn.li/u2ui/u2@main/el/parallaxbg/tests/visible.html)
44
+ [minimal.html](http://gcdn.li/u2ui/u2@main/el/parallaxbg/tests/minimal.html)
45
+ [demo.html](http://gcdn.li/u2ui/u2@main/el/parallaxbg/tests/demo.html)
46
46
 
47
47
  ## Also interesting
48
48
 
@@ -3,7 +3,7 @@
3
3
  "readme": "README.md",
4
4
  "modules": [{
5
5
  "kind": "javascript-module",
6
- "path": "parallax-bg.js",
6
+ "path": "parallaxbg.js",
7
7
  "declarations": [{
8
8
  "kind": "class",
9
9
  "description": "Scroll-driven parallax background layer with dynamic container sizing",
@@ -12,20 +12,20 @@
12
12
  "events": [],
13
13
  "attributes": [],
14
14
  "cssProperties": [{
15
- "name": "--parallax-bg-speed",
15
+ "name": "--parallaxbg-speed",
16
16
  "description": "Parallax speed factor (1 = normal scroll speed)",
17
17
  "syntax": "<number>"
18
18
  }],
19
19
  "superclass": { "name": "HTMLElement" },
20
- "tagName": "u2-parallax-bg",
20
+ "tagName": "u2-parallaxbg",
21
21
  "customElement": true
22
22
  }],
23
23
  "exports": [{
24
24
  "kind": "custom-element-definition",
25
- "name": "u2-parallax-bg",
25
+ "name": "u2-parallaxbg",
26
26
  "declaration": {
27
27
  "name": "U2ParallaxBg",
28
- "module": "parallax-bg.js"
28
+ "module": "parallaxbg.js"
29
29
  }
30
30
  }]
31
31
  }]
@@ -1,10 +1,10 @@
1
- .u2-parallax-bg-stage {
1
+ .u2-parallaxbg-stage {
2
2
  position:relative;
3
3
  display: flow-root; /* prevent margin collapse on inner element */
4
4
  z-index:0; /* needed? else the parallax-element will go behinde everithing with a stackingcontext? */
5
5
  }
6
6
 
7
- u2-parallax-bg {
7
+ u2-parallaxbg {
8
8
  position:absolute;
9
9
  inset: -.4px; /* ugly strange 1px space see demo */
10
10
  z-index:-1;
@@ -107,7 +107,7 @@ export default class U2ParallaxBg extends HTMLElement {
107
107
  this.visible = this.shadowRoot.querySelector('.visible');
108
108
 
109
109
  const style = getComputedStyle(this);
110
- const speed = style.getPropertyValue('--parallax-bg-speed');
110
+ const speed = style.getPropertyValue('--parallaxbg-speed');
111
111
  this.speed = speed === '' ? .5 : parseFloat(speed);
112
112
  }
113
113
  connectedCallback() {
@@ -149,13 +149,13 @@ export default class U2ParallaxBg extends HTMLElement {
149
149
  this.mover.style.bottom = -offset + 'px';
150
150
 
151
151
  // if the element cannot go further down or up
152
- // the [parallax-bg-visible] element
152
+ // the [parallaxbg-visible] element
153
153
  // the most complicated part of the lib, seems to work well, but it was born by trial and error
154
154
  if (this.speed < 0) {
155
- console.warn('parallax-bg: parallax-bg-visible attribute is not implemented for speed < 0');
155
+ console.warn('parallaxbg: parallaxbg-visible attribute is not implemented for speed < 0');
156
156
  return;
157
157
  }
158
- //const visibleEl = this.querySelector('[parallax-bg-visible]');
158
+ //const visibleEl = this.querySelector('[parallaxbg-visible]');
159
159
  const visibleEl = this.visible;
160
160
  if (visibleEl) {
161
161
  let top = 0;
@@ -194,4 +194,4 @@ export default class U2ParallaxBg extends HTMLElement {
194
194
  }
195
195
  }
196
196
 
197
- customElements.get('u2-parallax-bg') || customElements.define('u2-parallax-bg', U2ParallaxBg)
197
+ customElements.get('u2-parallaxbg') || customElements.define('u2-parallaxbg', U2ParallaxBg)
@@ -449,7 +449,7 @@ Line 3</pre>
449
449
  <img src="https://picsum.photos/id/20/400/200" alt="Slide 2">
450
450
  <img src="https://picsum.photos/id/30/400/200" alt="Slide 3">
451
451
  </u2-carousel>
452
- <u2-carousel-nav for="myCarousel"></u2-carousel-nav>
452
+ <u2-carouselnav for="myCarousel"></u2-carouselnav>
453
453
 
454
454
  <hr>
455
455
 
@@ -468,8 +468,8 @@ Line 3</pre>
468
468
  <hr>
469
469
 
470
470
  <u2-calendar>
471
- <u2-calendaritem date="2025-12-24">Christmas Eve</u2-calendaritem>
472
- <u2-calendaritem date="2025-12-31">New Year's Eve</u2-calendaritem>
471
+ <u2-calendar-item date="2025-12-24">Christmas Eve</u2-calendar-item>
472
+ <u2-calendar-item date="2025-12-31">New Year's Eve</u2-calendar-item>
473
473
  </u2-calendar>
474
474
 
475
475
  <hr>
@@ -568,9 +568,9 @@ Line 3</pre>
568
568
  <hr>
569
569
 
570
570
  <div style="height: 300px; position: relative; overflow: hidden;">
571
- <u2-parallax-bg>
571
+ <u2-parallaxbg>
572
572
  <img src="https://picsum.photos/id/50/800/600" alt="Parallax">
573
- </u2-parallax-bg>
573
+ </u2-parallaxbg>
574
574
  <h2 style="position: relative; color: white;">Content on Top</h2>
575
575
  </div>
576
576
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u2ui",
3
- "version": "1.5.20",
3
+ "version": "1.7.5",
4
4
  "description": "HTML Advanced: More Capabilities, Same Feel, Same Simplicity",
5
5
  "keywords": ["web-components","pattern-library","component-library","ui-toolkit","design-system"],
6
6
  "author": "Tobias Buschor",
package/u2/enhance.js CHANGED
@@ -98,7 +98,8 @@ export function enhance(root, { registry = root.customElementRegistry ?? customE
98
98
  // neither a DOMTokenList nor a NamedNodeMap iterator is allocated per element.
99
99
  function visit(el) {
100
100
  const tag = el.tagName;
101
- if (tag.startsWith('U2-')) load('el', tag.slice(3).toLowerCase());
101
+ // the project is the part up to the first hyphen: u2-calendar-item comes with el/calendar
102
+ if (tag.startsWith('U2-')) load('el', tag.slice(3).toLowerCase().replace(/-.*/, ''));
102
103
 
103
104
  const cls = el.className; // string on html elements, SVGAnimatedString on svg
104
105
  if (typeof cls === 'string' && cls.includes('u2-')) {
package/u2/projects.json CHANGED
@@ -246,15 +246,7 @@
246
246
  },
247
247
  "calendar": {
248
248
  "name": "calendar",
249
- "description": "Calendar / month view component, use `<u2-calendaritem>` as children",
250
- "beta": false,
251
- "deprecated": false,
252
- "css": true,
253
- "js": true
254
- },
255
- "parallax-bg": {
256
- "name": "parallax-bg",
257
- "description": "Scroll-driven parallax background-layer with dynamic container sizing for full content visibility",
249
+ "description": "Calendar / month view component, use `<u2-calendar-item>` as children",
258
250
  "beta": false,
259
251
  "deprecated": false,
260
252
  "css": true,
@@ -284,6 +276,14 @@
284
276
  "css": true,
285
277
  "js": true
286
278
  },
279
+ "carouselnav": {
280
+ "name": "carouselnav",
281
+ "description": "Carousel navigation element for u2-carousel. (beta)",
282
+ "beta": true,
283
+ "deprecated": false,
284
+ "css": false,
285
+ "js": true
286
+ },
287
287
  "fields": {
288
288
  "name": "fields",
289
289
  "description": "Every content before a form element is considered as label",
@@ -332,6 +332,14 @@
332
332
  "css": true,
333
333
  "js": true
334
334
  },
335
+ "parallaxbg": {
336
+ "name": "parallaxbg",
337
+ "description": "Scroll-driven parallax background-layer with dynamic container sizing for full content visibility",
338
+ "beta": false,
339
+ "deprecated": false,
340
+ "css": true,
341
+ "js": true
342
+ },
335
343
  "carousel": {
336
344
  "name": "carousel",
337
345
  "description": "Simple carousel component",
@@ -492,14 +500,6 @@
492
500
  "css": true,
493
501
  "js": true
494
502
  },
495
- "carousel-nav": {
496
- "name": "carousel-nav",
497
- "description": "Carousel navigation element for u2-carousel. (beta)",
498
- "beta": true,
499
- "deprecated": false,
500
- "css": false,
501
- "js": true
502
- },
503
503
  "accordion": {
504
504
  "name": "accordion",
505
505
  "description": "Turns HTML structures with headings into an accordion, using the content between headings as each panel’s content.",