@canonical/react-ds-app-launchpad 0.9.0-experimental.5 → 0.9.0-experimental.7

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 (66) hide show
  1. package/dist/esm/ui/EditableBlock/Context.js +4 -0
  2. package/dist/esm/ui/EditableBlock/Context.js.map +1 -0
  3. package/dist/esm/ui/EditableBlock/EditableBlock.js +33 -0
  4. package/dist/esm/ui/EditableBlock/EditableBlock.js.map +1 -0
  5. package/dist/esm/ui/EditableBlock/EditableBlock.test.js +57 -0
  6. package/dist/esm/ui/EditableBlock/EditableBlock.test.js.map +1 -0
  7. package/dist/esm/ui/EditableBlock/hooks/index.js +2 -0
  8. package/dist/esm/ui/EditableBlock/hooks/index.js.map +1 -0
  9. package/dist/esm/ui/EditableBlock/hooks/useEditableBlock.js +11 -0
  10. package/dist/esm/ui/EditableBlock/hooks/useEditableBlock.js.map +1 -0
  11. package/dist/esm/ui/EditableBlock/index.js +3 -0
  12. package/dist/esm/ui/EditableBlock/index.js.map +1 -0
  13. package/dist/esm/ui/EditableBlock/styles.css +41 -0
  14. package/dist/esm/ui/EditableBlock/types.js +2 -0
  15. package/dist/esm/ui/EditableBlock/types.js.map +1 -0
  16. package/dist/esm/ui/RelativeTime/RelativeTime.js +66 -0
  17. package/dist/esm/ui/RelativeTime/RelativeTime.js.map +1 -0
  18. package/dist/esm/ui/RelativeTime/index.js +4 -0
  19. package/dist/esm/ui/RelativeTime/index.js.map +1 -0
  20. package/dist/esm/ui/RelativeTime/types.js +2 -0
  21. package/dist/esm/ui/RelativeTime/types.js.map +1 -0
  22. package/dist/esm/ui/RelativeTime/utils/constants.js +6 -0
  23. package/dist/esm/ui/RelativeTime/utils/constants.js.map +1 -0
  24. package/dist/esm/ui/RelativeTime/utils/formatHumanTime.js +43 -0
  25. package/dist/esm/ui/RelativeTime/utils/formatHumanTime.js.map +1 -0
  26. package/dist/esm/ui/RelativeTime/utils/getOptimalUpdateInterval.js +24 -0
  27. package/dist/esm/ui/RelativeTime/utils/getOptimalUpdateInterval.js.map +1 -0
  28. package/dist/esm/ui/RelativeTime/utils/index.js +4 -0
  29. package/dist/esm/ui/RelativeTime/utils/index.js.map +1 -0
  30. package/dist/esm/ui/RelativeTime/utils/parseInstant.js +22 -0
  31. package/dist/esm/ui/RelativeTime/utils/parseInstant.js.map +1 -0
  32. package/dist/esm/ui/index.js +1 -0
  33. package/dist/esm/ui/index.js.map +1 -1
  34. package/dist/types/ui/EditableBlock/Context.d.ts +4 -0
  35. package/dist/types/ui/EditableBlock/Context.d.ts.map +1 -0
  36. package/dist/types/ui/EditableBlock/EditableBlock.d.ts +10 -0
  37. package/dist/types/ui/EditableBlock/EditableBlock.d.ts.map +1 -0
  38. package/dist/types/ui/EditableBlock/EditableBlock.test.d.ts +2 -0
  39. package/dist/types/ui/EditableBlock/EditableBlock.test.d.ts.map +1 -0
  40. package/dist/types/ui/EditableBlock/hooks/index.d.ts +2 -0
  41. package/dist/types/ui/EditableBlock/hooks/index.d.ts.map +1 -0
  42. package/dist/types/ui/EditableBlock/hooks/useEditableBlock.d.ts +4 -0
  43. package/dist/types/ui/EditableBlock/hooks/useEditableBlock.d.ts.map +1 -0
  44. package/dist/types/ui/EditableBlock/index.d.ts +3 -0
  45. package/dist/types/ui/EditableBlock/index.d.ts.map +1 -0
  46. package/dist/types/ui/EditableBlock/types.d.ts +24 -0
  47. package/dist/types/ui/EditableBlock/types.d.ts.map +1 -0
  48. package/dist/types/ui/RelativeTime/RelativeTime.d.ts +11 -0
  49. package/dist/types/ui/RelativeTime/RelativeTime.d.ts.map +1 -0
  50. package/dist/types/ui/RelativeTime/index.d.ts +3 -0
  51. package/dist/types/ui/RelativeTime/index.d.ts.map +1 -0
  52. package/dist/types/ui/RelativeTime/types.d.ts +36 -0
  53. package/dist/types/ui/RelativeTime/types.d.ts.map +1 -0
  54. package/dist/types/ui/RelativeTime/utils/constants.d.ts +6 -0
  55. package/dist/types/ui/RelativeTime/utils/constants.d.ts.map +1 -0
  56. package/dist/types/ui/RelativeTime/utils/formatHumanTime.d.ts +21 -0
  57. package/dist/types/ui/RelativeTime/utils/formatHumanTime.d.ts.map +1 -0
  58. package/dist/types/ui/RelativeTime/utils/getOptimalUpdateInterval.d.ts +8 -0
  59. package/dist/types/ui/RelativeTime/utils/getOptimalUpdateInterval.d.ts.map +1 -0
  60. package/dist/types/ui/RelativeTime/utils/index.d.ts +4 -0
  61. package/dist/types/ui/RelativeTime/utils/index.d.ts.map +1 -0
  62. package/dist/types/ui/RelativeTime/utils/parseInstant.d.ts +9 -0
  63. package/dist/types/ui/RelativeTime/utils/parseInstant.d.ts.map +1 -0
  64. package/dist/types/ui/index.d.ts +1 -0
  65. package/dist/types/ui/index.d.ts.map +1 -1
  66. package/package.json +3 -2
@@ -0,0 +1,4 @@
1
+ import { createContext } from "react";
2
+ const EditingContext = createContext(undefined);
3
+ export default EditingContext;
4
+ //# sourceMappingURL=Context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Context.js","sourceRoot":"","sources":["../../../../src/ui/EditableBlock/Context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAGtC,MAAM,cAAc,GAAG,aAAa,CAAiC,SAAS,CAAC,CAAC;AAEhF,eAAe,cAAc,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useRef, useState } from "react";
3
+ import EditingContext from "./Context.js";
4
+ import "./styles.css";
5
+ /**
6
+ * Component that renders toggling edit mode block
7
+ * @returns {React.ReactElement} - Rendered EditableBlock
8
+ */
9
+ const EditableBlock = ({ id, EditComponent, className: userClassName, style, title, tag: TitleTag = "h3", }) => {
10
+ const [isEditing, setIsEditing] = useState(false);
11
+ const isFocusedRef = useRef(false);
12
+ const toggleEditing = useCallback(() => {
13
+ setIsEditing((editing) => !editing);
14
+ }, []);
15
+ const handleBlur = useCallback(() => {
16
+ isFocusedRef.current = false;
17
+ }, []);
18
+ const handleFocus = useCallback(() => {
19
+ isFocusedRef.current = true;
20
+ }, []);
21
+ const handleKeyUp = useCallback((event) => {
22
+ if ((isFocusedRef.current && event.key === "Enter") ||
23
+ event.key === " ") {
24
+ toggleEditing();
25
+ }
26
+ }, [toggleEditing]);
27
+ const componentCssClassName = "ds editable-block";
28
+ return (_jsx(EditingContext.Provider, { value: { isEditing, toggleEditing }, children: _jsxs("div", { className: [componentCssClassName, userClassName]
29
+ .filter(Boolean)
30
+ .join(" "), style: style, id: id, children: [_jsxs("header", { children: [_jsx(TitleTag, { className: "title", children: title }), _jsx("div", { className: `icon ${isEditing ? "icon-close" : "icon-edit"}`, onClick: toggleEditing, onKeyUp: handleKeyUp, onBlur: handleBlur, onFocus: handleFocus, role: "button", tabIndex: 0 })] }), _jsx("div", { className: "content", children: EditComponent && (_jsx(EditComponent, { ...{ isEditing, toggleEditing } })) })] }) }));
31
+ };
32
+ export default EditableBlock;
33
+ //# sourceMappingURL=EditableBlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditableBlock.js","sourceRoot":"","sources":["../../../../src/ui/EditableBlock/EditableBlock.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,cAAc,MAAM,cAAc,CAAC;AAG1C,OAAO,cAAc,CAAC;AAEtB;;;GAGG;AAEH,MAAM,aAAa,GAAG,CAA6B,EACjD,EAAE,EACF,aAAa,EACb,SAAS,EAAE,aAAa,EACxB,KAAK,EACL,KAAK,EACL,GAAG,EAAE,QAAQ,GAAG,IAAI,GACE,EAAsB,EAAE;IAC9C,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,MAAM,CAAU,KAAK,CAAC,CAAC;IAE5C,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;QACrC,YAAY,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;QAClC,YAAY,CAAC,OAAO,GAAG,KAAK,CAAC;IAC/B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,YAAY,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,WAAW,GAAG,WAAW,CAC7B,CAAC,KAA0B,EAAE,EAAE;QAC7B,IACE,CAAC,YAAY,CAAC,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC;YAC/C,KAAK,CAAC,GAAG,KAAK,GAAG,EACjB,CAAC;YACD,aAAa,EAAE,CAAC;QAClB,CAAC;IACH,CAAC,EACD,CAAC,aAAa,CAAC,CAChB,CAAC;IAEF,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;IAElD,OAAO,CACL,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,YAC1D,eACE,SAAS,EAAE,CAAC,qBAAqB,EAAE,aAAa,CAAC;iBAC9C,MAAM,CAAC,OAAO,CAAC;iBACf,IAAI,CAAC,GAAG,CAAC,EACZ,KAAK,EAAE,KAAK,EACZ,EAAE,EAAE,EAAE,aAEN,6BACE,KAAC,QAAQ,IAAC,SAAS,EAAC,OAAO,YAAE,KAAK,GAAY,EAC9C,cACE,SAAS,EAAE,QAAQ,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,EAAE,EAC3D,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,CAAC,GACX,IACK,EACT,cAAK,SAAS,EAAC,SAAS,YACrB,aAAa,IAAI,CAChB,KAAC,aAAa,OAAM,EAAE,SAAS,EAAE,aAAa,EAAQ,GAAI,CAC3D,GACG,IACF,GACkB,CAC3B,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /* @canonical/generator-ds 0.8.0-experimental.0 */
3
+ import { fireEvent, render, screen } from "@testing-library/react";
4
+ import { describe, expect, it } from "vitest";
5
+ import EditableBlock from "./EditableBlock.js";
6
+ import { useEditableBlock } from "./hooks/useEditableBlock.js";
7
+ const SampleEditComponent = () => {
8
+ return _jsx("div", { children: "Sample" });
9
+ };
10
+ describe("EditableBlock component", () => {
11
+ it("renders children content", () => {
12
+ render(_jsx(EditableBlock, { title: "Hello world!", EditComponent: SampleEditComponent }));
13
+ const sampleElement = screen.getByText("Sample");
14
+ expect(sampleElement).not.toBeNull();
15
+ expect(document.body.contains(sampleElement)).toBe(true);
16
+ });
17
+ it("toggles editing state when icon is clicked", () => {
18
+ render(_jsx(EditableBlock, { title: "Hello world!", EditComponent: SampleEditComponent }));
19
+ const editIcon = screen.getByRole("button");
20
+ fireEvent.click(editIcon);
21
+ expect(editIcon.classList.contains("icon-close")).toBe(true);
22
+ fireEvent.click(editIcon);
23
+ expect(editIcon.classList.contains("icon-edit")).toBe(true);
24
+ });
25
+ it("toggles editing state when Enter key is pressed", () => {
26
+ render(_jsx(EditableBlock, { title: "Hello world!", EditComponent: SampleEditComponent }));
27
+ const editIcon = screen.getByRole("button");
28
+ fireEvent.keyUp(editIcon, { key: "Enter", code: "Enter", charCode: 13 });
29
+ expect(editIcon.classList.contains("icon-close")).toBe(true);
30
+ fireEvent.keyUp(editIcon, { key: "Enter", code: "Enter", charCode: 13 });
31
+ expect(editIcon.classList.contains("icon-edit")).toBe(true);
32
+ });
33
+ it("toggles editing state when Space key is pressed", () => {
34
+ render(_jsx(EditableBlock, { title: "Hello world!", EditComponent: SampleEditComponent }));
35
+ const editIcon = screen.getByRole("button");
36
+ fireEvent.keyUp(editIcon, { key: " ", code: "Space", charCode: 32 });
37
+ expect(editIcon.classList.contains("icon-close")).toBe(true);
38
+ fireEvent.keyUp(editIcon, { key: " ", code: "Space", charCode: 32 });
39
+ expect(editIcon.classList.contains("icon-edit")).toBe(true);
40
+ });
41
+ it("provides editing context to children", () => {
42
+ const ChildComponent = () => {
43
+ const { isEditing } = useEditableBlock();
44
+ return _jsx("div", { children: isEditing ? "Editing" : "Not Editing" });
45
+ };
46
+ render(_jsx(EditableBlock, { title: "Hello world!", EditComponent: ChildComponent }));
47
+ const notEditingElement = screen.getByText("Not Editing");
48
+ expect(notEditingElement).not.toBeNull();
49
+ expect(document.body.contains(notEditingElement)).toBe(true);
50
+ const editIcon = screen.getByRole("button");
51
+ fireEvent.click(editIcon);
52
+ const editingElement = screen.getByText("Editing");
53
+ expect(editingElement).not.toBeNull();
54
+ expect(document.body.contains(editingElement)).toBe(true);
55
+ });
56
+ });
57
+ //# sourceMappingURL=EditableBlock.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditableBlock.test.js","sourceRoot":"","sources":["../../../../src/ui/EditableBlock/EditableBlock.test.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from "./useEditableBlock.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/ui/EditableBlock/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { useContext } from "react";
2
+ import EditingContext from "../Context.js";
3
+ export const useEditableBlock = () => {
4
+ const context = useContext(EditingContext);
5
+ if (!context) {
6
+ throw new Error("useEditing cannot be used directly.");
7
+ }
8
+ return context;
9
+ };
10
+ export default useEditableBlock;
11
+ //# sourceMappingURL=useEditableBlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEditableBlock.js","sourceRoot":"","sources":["../../../../../src/ui/EditableBlock/hooks/useEditableBlock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,cAAc,MAAM,eAAe,CAAC;AAG3C,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAuB,EAAE;IACvD,MAAM,OAAO,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,3 @@
1
+ /* @canonical/generator-ds 0.8.0-experimental.0 */
2
+ export { default as EditableBlock } from "./EditableBlock.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/ui/EditableBlock/index.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,41 @@
1
+ .ds.editable-block {
2
+ --editable-block-header-icon-size: 16px;
3
+ --editable-block-header-icon-edit-url: url("data:image/svg+xml,%3Csvg width='16' height='16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14 13.25v1.5H2v-1.5h12zM11.907 1.676l.58.58a2 2 0 010 2.829l-5.133 5.133c-.038.038-.079.073-.122.104l.01.008a3.77 3.77 0 01-1.718.983l-3.52.914.863-3.583c.138-.57.41-1.099.795-1.54l.17-.183.01.01c.03-.043.065-.084.103-.122l5.133-5.133a2 2 0 012.829 0zm-3.309 2.6L5.036 7.84l-.593.82 1.062 1.063.814-.591 3.567-3.568-1.288-1.288zm1.61-1.597l-.07.057-.479.48 1.288 1.288.48-.48a.5.5 0 00.057-.638l-.057-.069-.581-.58a.5.5 0 00-.638-.058z' fill='%23000000' fill-rule='nonzero'/%3E%3C/svg%3E");
4
+ --editable-block-header-icon-close-url: url("data:image/svg+xml,%3Csvg width='16' height='16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23000000' fill-rule='nonzero' d='M13.041 1.898l1.06 1.06L9.062 8l5.04 5.042-1.06 1.06L8 9.062 2.96 14.1l-1.06-1.06L6.938 8 1.9 2.96l1.06-1.06 5.04 5.04z'/%3E%3C/svg%3E");
5
+ --editable-block-header-title-font-weight: 550;
6
+
7
+ > header {
8
+ display: flex;
9
+ justify-content: space-between;
10
+ align-items: center;
11
+
12
+ > .icon {
13
+ width: var(--editable-block-header-icon-size);
14
+ height: var(--editable-block-header-icon-size);
15
+ background-size: contain;
16
+ background-repeat: no-repeat;
17
+ cursor: pointer;
18
+ }
19
+
20
+ > .icon-edit {
21
+ background-image: var(--editable-block-header-icon-edit-url);
22
+ }
23
+
24
+ > .icon-close {
25
+ background-image: var(--editable-block-header-icon-close-url);
26
+ }
27
+
28
+ > .title {
29
+ flex: 1;
30
+ text-align: left;
31
+ font-weight: var(--editable-block-header-title-font-weight);
32
+ }
33
+ }
34
+
35
+ > .content {
36
+ flex-grow: 1;
37
+ display: flex;
38
+ justify-content: center;
39
+ align-items: center;
40
+ }
41
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/ui/EditableBlock/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { formatHumanTime, getOptimalUpdateInterval, parseInstant, } from "./utils/index.js";
4
+ const componentCssClassName = "ds relative-time";
5
+ /**
6
+ * The RelativeTime component displays timestamps in a human-readable relative format (like "2 hours ago" or "in 3 days").
7
+ *
8
+ * It features automatic live updates with interval optimization based on time distance, configurable "now" threshold,
9
+ * and renders as proper semantic HTML using the <time> element. The component leverages the Temporal API for calculations and Intl.RelativeTimeFormat for localization.
10
+ */
11
+ const RelativeTime = ({ id, className, style, time, relativeTimeFormat, nowThreshold = 10, disableLiveUpdate = false, nowKeyword = "now", invalidDateKeyword = "invalid date", }) => {
12
+ const [relativeTime, setRelativeTime] = useState("");
13
+ const [localeTimeString, setLocaleTimeString] = useState("");
14
+ const [stringTime, setStringTime] = useState("");
15
+ const intervalRef = useRef(null);
16
+ const instantTime = useMemo(() => {
17
+ try {
18
+ return parseInstant(time);
19
+ }
20
+ catch (error) {
21
+ console.error(error);
22
+ return null;
23
+ }
24
+ }, [time]);
25
+ const updateRelativeTime = useCallback(() => {
26
+ if (instantTime === null) {
27
+ setRelativeTime(invalidDateKeyword);
28
+ setLocaleTimeString("");
29
+ setStringTime("");
30
+ return null;
31
+ }
32
+ const timeFormatter = relativeTimeFormat ||
33
+ new Intl.RelativeTimeFormat(undefined, { numeric: "auto" });
34
+ setRelativeTime(formatHumanTime(instantTime, {
35
+ nowThreshold,
36
+ nowKeyword,
37
+ relativeTimeFormat: timeFormatter,
38
+ }));
39
+ setLocaleTimeString(instantTime.toLocaleString());
40
+ setStringTime(instantTime.toString());
41
+ }, [
42
+ instantTime,
43
+ relativeTimeFormat,
44
+ nowThreshold,
45
+ nowKeyword,
46
+ invalidDateKeyword,
47
+ ]);
48
+ useEffect(() => {
49
+ // initial calculation
50
+ updateRelativeTime();
51
+ if (disableLiveUpdate || instantTime === null)
52
+ return;
53
+ const updateInterval = getOptimalUpdateInterval(instantTime);
54
+ // SSR check
55
+ if (typeof window === "undefined")
56
+ return;
57
+ intervalRef.current = window.setInterval(updateRelativeTime, updateInterval);
58
+ return () => {
59
+ if (intervalRef.current)
60
+ window.clearInterval(intervalRef.current);
61
+ };
62
+ }, [instantTime, disableLiveUpdate, updateRelativeTime]);
63
+ return (_jsx("time", { id: id, style: style, className: [componentCssClassName, className].filter(Boolean).join(" "), title: localeTimeString, dateTime: stringTime, children: relativeTime }));
64
+ };
65
+ export default RelativeTime;
66
+ //# sourceMappingURL=RelativeTime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelativeTime.js","sourceRoot":"","sources":["../../../../src/ui/RelativeTime/RelativeTime.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /* @canonical/generator-ds 0.9.0-experimental.4 */
2
+ export { default as RelativeTime } from "./RelativeTime.js";
3
+ export * from "./types.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/ui/RelativeTime/index.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC5D,cAAc,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/ui/RelativeTime/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ export const MINUTE_IN_SECONDS = 60;
2
+ export const HOUR_IN_SECONDS = MINUTE_IN_SECONDS * 60;
3
+ export const DAY_IN_SECONDS = HOUR_IN_SECONDS * 24;
4
+ export const MONTH_IN_SECONDS = DAY_IN_SECONDS * 30;
5
+ export const YEAR_IN_SECONDS = DAY_IN_SECONDS * 365;
6
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../../src/ui/RelativeTime/utils/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AACpC,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,GAAG,EAAE,CAAC;AACtD,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,GAAG,EAAE,CAAC;AACnD,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,GAAG,EAAE,CAAC;AACpD,MAAM,CAAC,MAAM,eAAe,GAAG,cAAc,GAAG,GAAG,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ /**
3
+ * Formats a given instant as a human-readable time relative to the current time.
4
+ * @param instant The instant to format.
5
+ * @param options The options to use for formatting.
6
+ * @returns A human-readable relative time string
7
+ */
8
+ function formatHumanTime(instant, options) {
9
+ const now = Temporal.Now.instant();
10
+ const deltaSeconds = instant.epochSeconds - now.epochSeconds;
11
+ const absDeltaSeconds = Math.abs(deltaSeconds);
12
+ if (absDeltaSeconds < options.nowThreshold) {
13
+ return options.nowKeyword;
14
+ }
15
+ const timeZone = Temporal.Now.timeZoneId() || "UTC";
16
+ const instantZDT = instant.toZonedDateTimeISO(timeZone);
17
+ const nowZDT = now.toZonedDateTimeISO(timeZone);
18
+ const diff = instantZDT.since(nowZDT, { largestUnit: "years" });
19
+ const units = [
20
+ "years",
21
+ "months",
22
+ "days",
23
+ "hours",
24
+ "minutes",
25
+ "seconds",
26
+ ];
27
+ let rtfUnit = "seconds";
28
+ let rtfValue = 0;
29
+ for (const unit of units) {
30
+ const value = diff[unit];
31
+ if (value !== 0) {
32
+ rtfUnit = unit;
33
+ rtfValue = value;
34
+ break;
35
+ }
36
+ }
37
+ if (rtfValue === 0) {
38
+ return options.nowKeyword;
39
+ }
40
+ return options.relativeTimeFormat.format(rtfValue, rtfUnit);
41
+ }
42
+ export default formatHumanTime;
43
+ //# sourceMappingURL=formatHumanTime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatHumanTime.js","sourceRoot":"","sources":["../../../../../src/ui/RelativeTime/utils/formatHumanTime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAgBjD;;;;;GAKG;AACH,SAAS,eAAe,CACtB,OAAyB,EACzB,OAAyB;IAEzB,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACnC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;IAC7D,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAE/C,IAAI,eAAe,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;QAC3C,OAAO,OAAO,CAAC,UAAU,CAAC;IAC5B,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,KAAK,CAAC;IACpD,MAAM,UAAU,GAAG,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,GAAG,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;IAEhE,MAAM,KAAK,GAAG;QACZ,OAAO;QACP,QAAQ;QACR,MAAM;QACN,OAAO;QACP,SAAS;QACT,SAAS;KAC8B,CAAC;IAE1C,IAAI,OAAO,GAAgC,SAAS,CAAC;IACrD,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QACzB,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,OAAO,GAAG,IAAI,CAAC;YACf,QAAQ,GAAG,KAAK,CAAC;YACjB,MAAM;QACR,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,OAAO,CAAC,UAAU,CAAC;IAC5B,CAAC;IAED,OAAO,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC;AAED,eAAe,eAAe,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ import * as constants from "./constants.js";
3
+ /**
4
+ * Calcualte the optimal update interval for a given instant based on the time distance from now.
5
+ * @returns The optimal update interval in milliseconds.
6
+ */
7
+ function getOptimalUpdateInterval(instant) {
8
+ const now = Temporal.Now.instant();
9
+ const deltaSeconds = instant.epochSeconds - now.epochSeconds;
10
+ const absDeltaSeconds = Math.abs(deltaSeconds);
11
+ if (absDeltaSeconds < constants.MINUTE_IN_SECONDS) {
12
+ return 1000;
13
+ }
14
+ if (absDeltaSeconds < constants.HOUR_IN_SECONDS) {
15
+ return constants.MINUTE_IN_SECONDS * 1000;
16
+ }
17
+ if (absDeltaSeconds < constants.DAY_IN_SECONDS) {
18
+ return constants.HOUR_IN_SECONDS * 1000;
19
+ }
20
+ // for any other time distance, update once a day
21
+ return constants.DAY_IN_SECONDS * 1000;
22
+ }
23
+ export default getOptimalUpdateInterval;
24
+ //# sourceMappingURL=getOptimalUpdateInterval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getOptimalUpdateInterval.js","sourceRoot":"","sources":["../../../../../src/ui/RelativeTime/utils/getOptimalUpdateInterval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAE5C;;;GAGG;AACH,SAAS,wBAAwB,CAAC,OAAyB;IACzD,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACnC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;IAC7D,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAE/C,IAAI,eAAe,GAAG,SAAS,CAAC,iBAAiB,EAAE,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,eAAe,GAAG,SAAS,CAAC,eAAe,EAAE,CAAC;QAChD,OAAO,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAC5C,CAAC;IAED,IAAI,eAAe,GAAG,SAAS,CAAC,cAAc,EAAE,CAAC;QAC/C,OAAO,SAAS,CAAC,eAAe,GAAG,IAAI,CAAC;IAC1C,CAAC;IACD,iDAAiD;IACjD,OAAO,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC;AACzC,CAAC;AAED,eAAe,wBAAwB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { default as formatHumanTime } from "./formatHumanTime.js";
2
+ export { default as getOptimalUpdateInterval } from "./getOptimalUpdateInterval.js";
3
+ export { default as parseInstant } from "./parseInstant.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/ui/RelativeTime/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ /**
3
+ * Parser for Temporal.Instant.
4
+ * @param time - instance, date or string to parse
5
+ * @throws Error if time is invalid
6
+ */
7
+ function parseInstant(time) {
8
+ if (time instanceof Temporal.Instant) {
9
+ return time;
10
+ }
11
+ if (time instanceof Date) {
12
+ return Temporal.Instant.fromEpochMilliseconds(time.getTime());
13
+ }
14
+ try {
15
+ return Temporal.Instant.from(time);
16
+ }
17
+ catch (e) {
18
+ throw new Error(`Invalid time string (ISO string or Date object expected): ${time}`);
19
+ }
20
+ }
21
+ export default parseInstant;
22
+ //# sourceMappingURL=parseInstant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseInstant.js","sourceRoot":"","sources":["../../../../../src/ui/RelativeTime/utils/parseInstant.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD;;;;GAIG;AACH,SAAS,YAAY,CAAC,IAAsC;IAC1D,IAAI,IAAI,YAAY,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;QACzB,OAAO,QAAQ,CAAC,OAAO,CAAC,qBAAqB,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,6DAA6D,IAAI,EAAE,CACpE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,eAAe,YAAY,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from "./GitDiffViewer/index.js";
2
+ export * from "./EditableBlock/index.js";
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { EditingContextType } from "./types.js";
2
+ declare const EditingContext: import("react").Context<EditingContextType | undefined>;
3
+ export default EditingContext;
4
+ //# sourceMappingURL=Context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Context.d.ts","sourceRoot":"","sources":["../../../../src/ui/EditableBlock/Context.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,QAAA,MAAM,cAAc,yDAA2D,CAAC;AAEhF,eAAe,cAAc,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type React from "react";
2
+ import type { EditElementProps, EditableBlockProps } from "./types.js";
3
+ import "./styles.css";
4
+ /**
5
+ * Component that renders toggling edit mode block
6
+ * @returns {React.ReactElement} - Rendered EditableBlock
7
+ */
8
+ declare const EditableBlock: <T extends EditElementProps>({ id, EditComponent, className: userClassName, style, title, tag: TitleTag, }: EditableBlockProps<T>) => React.ReactElement;
9
+ export default EditableBlock;
10
+ //# sourceMappingURL=EditableBlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditableBlock.d.ts","sourceRoot":"","sources":["../../../../src/ui/EditableBlock/EditableBlock.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvE,OAAO,cAAc,CAAC;AAEtB;;;GAGG;AAEH,QAAA,MAAM,aAAa,GAAI,CAAC,SAAS,gBAAgB,iFAO9C,kBAAkB,CAAC,CAAC,CAAC,KAAG,KAAK,CAAC,YA2DhC,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=EditableBlock.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditableBlock.test.d.ts","sourceRoot":"","sources":["../../../../src/ui/EditableBlock/EditableBlock.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export * from "./useEditableBlock.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/ui/EditableBlock/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { EditingContextType } from "../types.js";
2
+ export declare const useEditableBlock: () => EditingContextType;
3
+ export default useEditableBlock;
4
+ //# sourceMappingURL=useEditableBlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEditableBlock.d.ts","sourceRoot":"","sources":["../../../../../src/ui/EditableBlock/hooks/useEditableBlock.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,eAAO,MAAM,gBAAgB,QAAO,kBAMnC,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { default as EditableBlock } from "./EditableBlock.js";
2
+ export type { default as EditableBlockProps } from "./types.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ui/EditableBlock/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC9D,YAAY,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type React from "react";
2
+ import type { JSX } from "react";
3
+ export interface EditingContextType {
4
+ isEditing: boolean;
5
+ toggleEditing: () => void;
6
+ }
7
+ export type EditElementProps = EditingContextType;
8
+ export type EditElement<T extends EditElementProps> = (props: T) => React.ReactNode;
9
+ export interface EditableBlockProps<T extends EditElementProps> {
10
+ /** Unique identifier for the editable block */
11
+ id?: string;
12
+ /** Component to render when in edit mode, must implement EditElement interface */
13
+ EditComponent: EditElement<T>;
14
+ /** CSS class name for additional styling */
15
+ className?: string;
16
+ /** Inline CSS styles to apply to the component */
17
+ style?: React.CSSProperties;
18
+ /** Title text for the editable block */
19
+ title?: string;
20
+ /** HTML element type to render as the title component (e.g., 'div', 'section') */
21
+ tag?: keyof JSX.IntrinsicElements;
22
+ }
23
+ export default EditableBlockProps;
24
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/ui/EditableBlock/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAEjC,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAElD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,gBAAgB,IAAI,CACpD,KAAK,EAAE,CAAC,KACL,KAAK,CAAC,SAAS,CAAC;AAErB,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,gBAAgB;IAC5D,+CAA+C;IAC/C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IAC9B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,iBAAiB,CAAC;CACnC;AAED,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type React from "react";
2
+ import type { RelativeTimeProps } from "./types.js";
3
+ /**
4
+ * The RelativeTime component displays timestamps in a human-readable relative format (like "2 hours ago" or "in 3 days").
5
+ *
6
+ * It features automatic live updates with interval optimization based on time distance, configurable "now" threshold,
7
+ * and renders as proper semantic HTML using the <time> element. The component leverages the Temporal API for calculations and Intl.RelativeTimeFormat for localization.
8
+ */
9
+ declare const RelativeTime: ({ id, className, style, time, relativeTimeFormat, nowThreshold, disableLiveUpdate, nowKeyword, invalidDateKeyword, }: RelativeTimeProps) => React.ReactElement;
10
+ export default RelativeTime;
11
+ //# sourceMappingURL=RelativeTime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelativeTime.d.ts","sourceRoot":"","sources":["../../../../src/ui/RelativeTime/RelativeTime.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AASpD;;;;;GAKG;AACH,QAAA,MAAM,YAAY,yHAUf,iBAAiB,KAAG,KAAK,CAAC,YA0E5B,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { default as RelativeTime } from "./RelativeTime.js";
2
+ export * from "./types.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ui/RelativeTime/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC5D,cAAc,YAAY,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { Temporal } from "@js-temporal/polyfill";
2
+ import type React from "react";
3
+ export interface RelativeTimeProps {
4
+ /** A unique identifier for the RelativeTime */
5
+ id?: string;
6
+ /** Additional CSS classes */
7
+ className?: string;
8
+ /** Inline styles */
9
+ style?: React.CSSProperties;
10
+ /**
11
+ * The time to be displayed as a relative time (ISO string or Date object).
12
+ */
13
+ time: string | Date | Temporal.Instant;
14
+ /**
15
+ * Disable live duration updates (default: false).
16
+ */
17
+ disableLiveUpdate?: boolean;
18
+ /**
19
+ * custom relative time format (default: local language and narrow style).
20
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat
21
+ */
22
+ relativeTimeFormat?: Intl.RelativeTimeFormat;
23
+ /**
24
+ * The threshold in seconds for when to display the relative time as "just now" (default is 10).
25
+ */
26
+ nowThreshold?: number;
27
+ /**
28
+ * The keyword to use for "now" (default: "now")
29
+ */
30
+ nowKeyword?: string;
31
+ /**
32
+ * The keyword to use when the given time is not a valid. (default: "invalid date")
33
+ */
34
+ invalidDateKeyword?: string;
35
+ }
36
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/ui/RelativeTime/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAE5B;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC;IAEvC;;OAEG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,IAAI,CAAC,kBAAkB,CAAC;IAE7C;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B"}
@@ -0,0 +1,6 @@
1
+ export declare const MINUTE_IN_SECONDS = 60;
2
+ export declare const HOUR_IN_SECONDS: number;
3
+ export declare const DAY_IN_SECONDS: number;
4
+ export declare const MONTH_IN_SECONDS: number;
5
+ export declare const YEAR_IN_SECONDS: number;
6
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../../src/ui/RelativeTime/utils/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,eAAe,QAAyB,CAAC;AACtD,eAAO,MAAM,cAAc,QAAuB,CAAC;AACnD,eAAO,MAAM,gBAAgB,QAAsB,CAAC;AACpD,eAAO,MAAM,eAAe,QAAuB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ type FormatterOptions = {
3
+ /** The threshold in seconds for considering the time as "now". */
4
+ nowThreshold: number;
5
+ /** The keyword to use for "now". */
6
+ nowKeyword: string;
7
+ /**
8
+ * The Intl.RelativeTimeFormat instance to use for formatting.
9
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat
10
+ */
11
+ relativeTimeFormat: Intl.RelativeTimeFormat;
12
+ };
13
+ /**
14
+ * Formats a given instant as a human-readable time relative to the current time.
15
+ * @param instant The instant to format.
16
+ * @param options The options to use for formatting.
17
+ * @returns A human-readable relative time string
18
+ */
19
+ declare function formatHumanTime(instant: Temporal.Instant, options: FormatterOptions): string;
20
+ export default formatHumanTime;
21
+ //# sourceMappingURL=formatHumanTime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatHumanTime.d.ts","sourceRoot":"","sources":["../../../../../src/ui/RelativeTime/utils/formatHumanTime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,KAAK,gBAAgB,GAAG;IACtB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IAErB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,CAAC;CAC7C,CAAC;AAEF;;;;;GAKG;AACH,iBAAS,eAAe,CACtB,OAAO,EAAE,QAAQ,CAAC,OAAO,EACzB,OAAO,EAAE,gBAAgB,GACxB,MAAM,CAwCR;AAED,eAAe,eAAe,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ /**
3
+ * Calcualte the optimal update interval for a given instant based on the time distance from now.
4
+ * @returns The optimal update interval in milliseconds.
5
+ */
6
+ declare function getOptimalUpdateInterval(instant: Temporal.Instant): number;
7
+ export default getOptimalUpdateInterval;
8
+ //# sourceMappingURL=getOptimalUpdateInterval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getOptimalUpdateInterval.d.ts","sourceRoot":"","sources":["../../../../../src/ui/RelativeTime/utils/getOptimalUpdateInterval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD;;;GAGG;AACH,iBAAS,wBAAwB,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,GAAG,MAAM,CAkBnE;AAED,eAAe,wBAAwB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { default as formatHumanTime } from "./formatHumanTime.js";
2
+ export { default as getOptimalUpdateInterval } from "./getOptimalUpdateInterval.js";
3
+ export { default as parseInstant } from "./parseInstant.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/ui/RelativeTime/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ /**
3
+ * Parser for Temporal.Instant.
4
+ * @param time - instance, date or string to parse
5
+ * @throws Error if time is invalid
6
+ */
7
+ declare function parseInstant(time: Temporal.Instant | Date | string): Temporal.Instant;
8
+ export default parseInstant;
9
+ //# sourceMappingURL=parseInstant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseInstant.d.ts","sourceRoot":"","sources":["../../../../../src/ui/RelativeTime/utils/parseInstant.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD;;;;GAIG;AACH,iBAAS,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,GAAG,IAAI,GAAG,MAAM,oBAc3D;AAED,eAAe,YAAY,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from "./GitDiffViewer/index.js";
2
+ export * from "./EditableBlock/index.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonical/react-ds-app-launchpad",
3
- "version": "0.9.0-experimental.5",
3
+ "version": "0.9.0-experimental.7",
4
4
  "type": "module",
5
5
  "module": "dist/esm/index.js",
6
6
  "types": "dist/types/index.d.ts",
@@ -41,6 +41,7 @@
41
41
  "@canonical/storybook-config": "^0.9.0-experimental.5",
42
42
  "@canonical/styles": "^0.9.0-experimental.2",
43
43
  "@canonical/utils": "^0.9.0-experimental.4",
44
+ "@js-temporal/polyfill": "^0.4.4",
44
45
  "highlight.js": "^11.11.1",
45
46
  "react": "^19.0.0",
46
47
  "react-dom": "^19.0.0"
@@ -68,5 +69,5 @@
68
69
  "vite-tsconfig-paths": "^5.1.4",
69
70
  "vitest": "^2.1.8"
70
71
  },
71
- "gitHead": "bc797d0c134200c372d31a8db89659caba2d6384"
72
+ "gitHead": "33b2a02e8c6c1befb93d206175a321c33c2064e5"
72
73
  }