@enigmax/primitives 0.13.0 → 0.14.1

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.
@@ -680,6 +680,11 @@ function RelativeTime({
680
680
  ...rest
681
681
  }) {
682
682
  useEffect(loadElement, []);
683
+ const applyPrefix = useCallback((node) => {
684
+ if (!node) return;
685
+ if (prefix === void 0 || prefix === "") node.removeAttribute("prefix");
686
+ else node.setAttribute("prefix", prefix);
687
+ }, [prefix]);
683
688
  const options = {
684
689
  format,
685
690
  tense,
@@ -712,7 +717,8 @@ function RelativeTime({
712
717
  if (view.absoluteOnly) {
713
718
  return createElement("time", { dateTime: view.iso, title: noTitle ? void 0 : view.exact, ...marks, ...rest }, view.label);
714
719
  }
715
- return createElement("relative-time", { ...relativeTimeAttributes(view, options), ...marks, ...rest }, view.label);
720
+ const { prefix: prefixAttribute, ...attributes } = relativeTimeAttributes(view, options);
721
+ return createElement("relative-time", { ...attributes, ...marks, ...rest, ref: applyPrefix }, view.label);
716
722
  }
717
723
 
718
724
  export { Button, Input, PasswordStrength, RelativeTime, Toaster, createNotificationQueue, defaultQueue, getLinkComponent, setLinkComponent, useButton, useInput, useMarquee, useNetworkState, useNotifications, useSearch };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { Button } from '../chunk-YFM2FWHQ.js';
3
- export { Input, PasswordStrength, RelativeTime, Toaster, createNotificationQueue, defaultQueue, getLinkComponent, setLinkComponent, useButton, useInput, useMarquee, useNetworkState, useNotifications, useSearch } from '../chunk-YFM2FWHQ.js';
2
+ import { Button } from '../chunk-QFJ5NT6Z.js';
3
+ export { Input, PasswordStrength, RelativeTime, Toaster, createNotificationQueue, defaultQueue, getLinkComponent, setLinkComponent, useButton, useInput, useMarquee, useNetworkState, useNotifications, useSearch } from '../chunk-QFJ5NT6Z.js';
4
4
  export { estimatePasswordStrength, generatePassword } from '../chunk-DD2X5B2J.js';
5
5
  import '../chunk-UZFEEFMF.js';
6
6
  import Link from 'next/link';
@@ -1,4 +1,4 @@
1
1
  "use client";
2
- export { Button, Input, PasswordStrength, RelativeTime, Toaster, createNotificationQueue, defaultQueue, getLinkComponent, setLinkComponent, useButton, useInput, useMarquee, useNetworkState, useNotifications, useSearch } from '../chunk-YFM2FWHQ.js';
2
+ export { Button, Input, PasswordStrength, RelativeTime, Toaster, createNotificationQueue, defaultQueue, getLinkComponent, setLinkComponent, useButton, useInput, useMarquee, useNetworkState, useNotifications, useSearch } from '../chunk-QFJ5NT6Z.js';
3
3
  export { estimatePasswordStrength, generatePassword } from '../chunk-DD2X5B2J.js';
4
4
  import '../chunk-UZFEEFMF.js';
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { Button } from '../chunk-YFM2FWHQ.js';
3
- export { Input, PasswordStrength, RelativeTime, Toaster, createNotificationQueue, defaultQueue, getLinkComponent, setLinkComponent, useButton, useInput, useMarquee, useNetworkState, useNotifications, useSearch } from '../chunk-YFM2FWHQ.js';
2
+ import { Button } from '../chunk-QFJ5NT6Z.js';
3
+ export { Input, PasswordStrength, RelativeTime, Toaster, createNotificationQueue, defaultQueue, getLinkComponent, setLinkComponent, useButton, useInput, useMarquee, useNetworkState, useNotifications, useSearch } from '../chunk-QFJ5NT6Z.js';
4
4
  export { estimatePasswordStrength, generatePassword } from '../chunk-DD2X5B2J.js';
5
5
  import '../chunk-UZFEEFMF.js';
6
6
  import { Link } from 'react-router';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enigmax/primitives",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "Headless components: the behaviour, the timing and the accessibility, with no styles of their own. Framework-agnostic core plus thin adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,7 +29,8 @@
29
29
  },
30
30
  "./relative-time.css": "./recipes/relative-time/styles.css",
31
31
  "./toast.css": "./recipes/toast/styles.css",
32
- "./input.css": "./recipes/input/styles.css"
32
+ "./input.css": "./recipes/input/styles.css",
33
+ "./marquee.css": "./recipes/marquee/styles.css"
33
34
  },
34
35
  "scripts": {
35
36
  "build": "tsup",
@@ -3,22 +3,29 @@
3
3
  *
4
4
  * import "@enigmax/utils/relative-time.css";
5
5
  *
6
+ * The element renders its output into a SHADOW ROOT, which is why these work the way they
7
+ * do: `text-transform` is an inherited property, so it crosses the boundary from the host
8
+ * and reaches text no outer selector could ever match. A descendant selector cannot - the
9
+ * light-DOM children are the fallback, not what you are looking at.
10
+ *
6
11
  * Both are opt-in per timestamp - the attributes only appear when you ask for them - and
7
- * both are marked !important on purpose: the element writes its own text content, so a
8
- * `text-transform` from a reset or a typography plugin lands on the same node and would
9
- * otherwise win on cascade order alone.
12
+ * both are !important on purpose: the transform lands on the host, where a reset or a
13
+ * typography plugin would otherwise win on cascade order alone.
10
14
  */
11
15
 
12
16
  /* Title Case Every Word. Reads oddly on "2 Days Ago"; usually you want the rule below. */
13
- [data-relative-time-capitalize="true"],
14
- [data-relative-time-capitalize="true"] * {
17
+ [data-relative-time-capitalize="true"] {
15
18
  text-transform: capitalize !important;
16
19
  }
17
20
 
18
- /* Just the first letter, so a standalone "yesterday" starts a line properly. The nested
19
- selectors cover the element wrapping its text in a child, which it does for some formats. */
20
- [data-relative-time-capitalize-first-letter="true"]::first-letter,
21
- [data-relative-time-capitalize-first-letter="true"] *::first-letter,
22
- [data-relative-time-capitalize-first-letter="true"] * *::first-letter {
21
+ /* Just the first letter, so a standalone "yesterday" starts a line properly.
22
+ `::first-letter` only applies to a BLOCK container and the element is inline, so the
23
+ host is made inline-block for it - scoped to the hook, so a timestamp that did not ask
24
+ for this keeps its inline layout. */
25
+ [data-relative-time-capitalize-first-letter="true"] {
26
+ display: inline-block;
27
+ }
28
+
29
+ [data-relative-time-capitalize-first-letter="true"]::first-letter {
23
30
  text-transform: uppercase !important;
24
31
  }
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { createElement, useEffect, type HTMLAttributes, type ReactNode } from "react";
3
+ import { createElement, useCallback, useEffect, type HTMLAttributes, type ReactNode } from "react";
4
4
  import { relativeTimeView, relativeTimeAttributes, type RelativeTimeOptions } from "@/core/relative-time";
5
5
 
6
6
  /**
@@ -75,6 +75,22 @@ export function RelativeTime({
75
75
  }: RelativeTimeProps): ReactNode {
76
76
  useEffect(loadElement, []);
77
77
 
78
+ /**
79
+ * `prefix` is set through the DOM, never as a React prop.
80
+ *
81
+ * `Element.prototype.prefix` is a read-only accessor (the XML namespace prefix), and
82
+ * React sets a KNOWN property rather than an attribute - so passing it through throws
83
+ * "Cannot set property prefix of #<Element> which has only a getter" and takes the
84
+ * whole tree down with it. It is the only attribute here that collides; the rest are
85
+ * either kebab-cased or writable. Applied on the ref, which runs before the custom
86
+ * element upgrades, so the element still reads it.
87
+ */
88
+ const applyPrefix = useCallback((node: HTMLElement | null) => {
89
+ if (!node) return;
90
+ if (prefix === undefined || prefix === "") node.removeAttribute("prefix");
91
+ else node.setAttribute("prefix", prefix);
92
+ }, [prefix]);
93
+
78
94
  const options: RelativeTimeOptions = {
79
95
  format, tense, precision, threshold, prefix, formatStyle, locale, timeZone,
80
96
  second, minute, hour, weekday, day, month, year, timeZoneName, noTitle, numericBeyondThreshold
@@ -99,5 +115,6 @@ export function RelativeTime({
99
115
  // createElement rather than JSX: `<relative-time>` is not a known intrinsic element, and
100
116
  // declaring it means augmenting React's JSX namespace from inside a published package -
101
117
  // which collides with any consumer that declared it too, differently.
102
- return createElement("relative-time", { ...relativeTimeAttributes(view, options), ...marks, ...rest }, view.label);
118
+ const { prefix: prefixAttribute, ...attributes } = relativeTimeAttributes(view, options);
119
+ return createElement("relative-time", { ...attributes, ...marks, ...rest, ref: applyPrefix }, view.label);
103
120
  }