@dbcdk/react-components 0.0.182 → 0.0.184
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.
- package/dist/client.cjs +21 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.js +3 -0
- package/dist/components/chip/Chip.module.css +3 -1
- package/dist/components/clear-button/ClearButton.cjs +7 -2
- package/dist/components/clear-button/ClearButton.d.ts +8 -1
- package/dist/components/clear-button/ClearButton.js +7 -2
- package/dist/components/clear-button/ClearButton.module.css +12 -0
- package/dist/components/forms/select/Select.cjs +2 -1
- package/dist/components/forms/select/Select.js +2 -1
- package/dist/components/json-viewer/JsonViewer.cjs +3 -2
- package/dist/components/json-viewer/JsonViewer.d.ts +1 -1
- package/dist/components/json-viewer/JsonViewer.js +3 -2
- package/dist/components/json-viewer/JsonViewer.module.css +7 -0
- package/dist/components/json-viewer/types.d.ts +6 -0
- package/dist/components/line-viewer/LineViewer.cjs +174 -0
- package/dist/components/line-viewer/LineViewer.d.ts +20 -0
- package/dist/components/line-viewer/LineViewer.js +168 -0
- package/dist/components/line-viewer/LineViewer.module.css +112 -0
- package/dist/components/line-viewer/tokenizeLineFormat.cjs +52 -0
- package/dist/components/line-viewer/tokenizeLineFormat.d.ts +14 -0
- package/dist/components/line-viewer/tokenizeLineFormat.js +50 -0
- package/dist/components/tree-view/TreeNodeRow.cjs +99 -0
- package/dist/components/tree-view/TreeNodeRow.d.ts +18 -0
- package/dist/components/tree-view/TreeNodeRow.js +93 -0
- package/dist/components/tree-view/TreeView.cjs +93 -0
- package/dist/components/tree-view/TreeView.d.ts +20 -0
- package/dist/components/tree-view/TreeView.js +79 -0
- package/dist/components/tree-view/TreeView.module.css +141 -0
- package/dist/components/tree-view/types.cjs +2 -0
- package/dist/components/tree-view/types.d.ts +44 -0
- package/dist/components/tree-view/types.js +1 -0
- package/dist/components/tree-view/useTreeExpansion.cjs +47 -0
- package/dist/components/tree-view/useTreeExpansion.d.ts +17 -0
- package/dist/components/tree-view/useTreeExpansion.js +45 -0
- package/dist/components/tree-view/utils.cjs +32 -0
- package/dist/components/tree-view/utils.d.ts +16 -0
- package/dist/components/tree-view/utils.js +29 -0
- package/dist/components/xml-viewer/XmlViewer.cjs +175 -0
- package/dist/components/xml-viewer/XmlViewer.d.ts +20 -0
- package/dist/components/xml-viewer/XmlViewer.js +169 -0
- package/dist/components/xml-viewer/XmlViewer.module.css +109 -0
- package/dist/components/xml-viewer/tokenizeXml.cjs +50 -0
- package/dist/components/xml-viewer/tokenizeXml.d.ts +12 -0
- package/dist/components/xml-viewer/tokenizeXml.js +48 -0
- package/dist/index.cjs +7 -0
- package/dist/index.css +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/utils/date/formatDate.cjs +29 -17
- package/dist/utils/date/formatDate.d.ts +7 -0
- package/dist/utils/date/formatDate.js +29 -17
- package/package.json +1 -1
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { TextWrap, ListTree, Code2, Sun, Moon, ClipboardCopy, MoreVertical } from 'lucide-react';
|
|
4
|
+
import { useMemo, useRef } from 'react';
|
|
5
|
+
import { Menu } from '../../components/menu/Menu';
|
|
6
|
+
import { Popover } from '../../components/popover/Popover';
|
|
7
|
+
import { usePersistentState } from '../../hooks/usePersistentState';
|
|
8
|
+
import { tokenizeXml } from './tokenizeXml';
|
|
9
|
+
import styles from './XmlViewer.module.css';
|
|
10
|
+
import { Button } from '../button/Button';
|
|
11
|
+
|
|
12
|
+
const TOKEN_CLASS = {
|
|
13
|
+
tagName: styles.tagName,
|
|
14
|
+
attrName: styles.attrName,
|
|
15
|
+
attrValue: styles.attrValue,
|
|
16
|
+
comment: styles.comment,
|
|
17
|
+
cdata: styles.cdata,
|
|
18
|
+
declaration: styles.declaration,
|
|
19
|
+
doctype: styles.doctype,
|
|
20
|
+
punctuation: styles.punctuation,
|
|
21
|
+
text: void 0
|
|
22
|
+
};
|
|
23
|
+
function XmlViewer({
|
|
24
|
+
xml,
|
|
25
|
+
size = "md",
|
|
26
|
+
appearance = "dark",
|
|
27
|
+
storageKey,
|
|
28
|
+
borderless = false
|
|
29
|
+
}) {
|
|
30
|
+
const tokens = useMemo(() => tokenizeXml(xml), [xml]);
|
|
31
|
+
const codeRef = useRef(null);
|
|
32
|
+
const { value: currentAppearance, setValue: setCurrentAppearance } = usePersistentState({
|
|
33
|
+
initialValue: appearance,
|
|
34
|
+
storageKey: storageKey ? `${storageKey}:appearance` : void 0,
|
|
35
|
+
validate: (stored) => stored === "dark" || stored === "light"
|
|
36
|
+
});
|
|
37
|
+
const { value: viewMode, setValue: setViewMode } = usePersistentState({
|
|
38
|
+
initialValue: "highlighted",
|
|
39
|
+
storageKey: storageKey ? `${storageKey}:viewMode` : void 0,
|
|
40
|
+
validate: (stored) => stored === "highlighted" || stored === "raw"
|
|
41
|
+
});
|
|
42
|
+
const { value: wrap, setValue: setWrap } = usePersistentState({
|
|
43
|
+
initialValue: true,
|
|
44
|
+
storageKey: storageKey ? `${storageKey}:wrap` : void 0,
|
|
45
|
+
validate: (stored) => typeof stored === "boolean"
|
|
46
|
+
});
|
|
47
|
+
const handleCopy = () => {
|
|
48
|
+
var _a;
|
|
49
|
+
(_a = navigator.clipboard) == null ? void 0 : _a.writeText(xml).catch(() => {
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
const handleDoubleClick = () => {
|
|
53
|
+
const el = codeRef.current;
|
|
54
|
+
const selection = typeof window !== "undefined" ? window.getSelection() : null;
|
|
55
|
+
if (!el || !selection) return;
|
|
56
|
+
const range = document.createRange();
|
|
57
|
+
range.selectNodeContents(el);
|
|
58
|
+
selection.removeAllRanges();
|
|
59
|
+
selection.addRange(range);
|
|
60
|
+
};
|
|
61
|
+
return /* @__PURE__ */ jsxs(
|
|
62
|
+
"section",
|
|
63
|
+
{
|
|
64
|
+
className: [styles.viewer, borderless ? styles.borderless : ""].filter(Boolean).join(" "),
|
|
65
|
+
"data-appearance": currentAppearance,
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ jsx(
|
|
68
|
+
"div",
|
|
69
|
+
{
|
|
70
|
+
className: styles.toolbar,
|
|
71
|
+
"data-surface": currentAppearance === "dark" ? "inverse" : void 0,
|
|
72
|
+
children: /* @__PURE__ */ jsx(
|
|
73
|
+
Popover,
|
|
74
|
+
{
|
|
75
|
+
matchTriggerWidth: false,
|
|
76
|
+
minWidth: "200px",
|
|
77
|
+
trigger: (toggle, _icon, isOpen) => /* @__PURE__ */ jsx(
|
|
78
|
+
Button,
|
|
79
|
+
{
|
|
80
|
+
variant: "inline",
|
|
81
|
+
"aria-label": "Flere handlinger",
|
|
82
|
+
"aria-haspopup": "menu",
|
|
83
|
+
"aria-expanded": isOpen ? true : false,
|
|
84
|
+
onClick: toggle,
|
|
85
|
+
children: /* @__PURE__ */ jsx(MoreVertical, { "aria-hidden": "true" })
|
|
86
|
+
}
|
|
87
|
+
),
|
|
88
|
+
children: (close) => /* @__PURE__ */ jsxs(Menu, { children: [
|
|
89
|
+
/* @__PURE__ */ jsx(Menu.Item, { children: /* @__PURE__ */ jsxs(
|
|
90
|
+
"button",
|
|
91
|
+
{
|
|
92
|
+
type: "button",
|
|
93
|
+
onClick: () => {
|
|
94
|
+
setWrap((value) => !value);
|
|
95
|
+
close();
|
|
96
|
+
},
|
|
97
|
+
children: [
|
|
98
|
+
/* @__PURE__ */ jsx(TextWrap, { "aria-hidden": "true" }),
|
|
99
|
+
/* @__PURE__ */ jsx("span", { children: wrap ? "Ombryd ikke tekst" : "Ombryd tekst" })
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
) }),
|
|
103
|
+
/* @__PURE__ */ jsx(Menu.Separator, {}),
|
|
104
|
+
/* @__PURE__ */ jsx(Menu.Item, { children: /* @__PURE__ */ jsxs(
|
|
105
|
+
"button",
|
|
106
|
+
{
|
|
107
|
+
type: "button",
|
|
108
|
+
onClick: () => {
|
|
109
|
+
setViewMode((mode) => mode === "raw" ? "highlighted" : "raw");
|
|
110
|
+
close();
|
|
111
|
+
},
|
|
112
|
+
children: [
|
|
113
|
+
viewMode === "raw" ? /* @__PURE__ */ jsx(ListTree, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Code2, { "aria-hidden": "true" }),
|
|
114
|
+
/* @__PURE__ */ jsx("span", { children: viewMode === "raw" ? "Vis med farver" : "Vis raw" })
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
) }),
|
|
118
|
+
/* @__PURE__ */ jsx(Menu.Item, { children: /* @__PURE__ */ jsxs(
|
|
119
|
+
"button",
|
|
120
|
+
{
|
|
121
|
+
type: "button",
|
|
122
|
+
onClick: () => {
|
|
123
|
+
setCurrentAppearance((value) => value === "dark" ? "light" : "dark");
|
|
124
|
+
close();
|
|
125
|
+
},
|
|
126
|
+
children: [
|
|
127
|
+
currentAppearance === "dark" ? /* @__PURE__ */ jsx(Sun, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Moon, { "aria-hidden": "true" }),
|
|
128
|
+
/* @__PURE__ */ jsx("span", { children: currentAppearance === "dark" ? "Lys visning" : "M\xF8rk visning" })
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
) }),
|
|
132
|
+
/* @__PURE__ */ jsx(Menu.Separator, {}),
|
|
133
|
+
/* @__PURE__ */ jsx(Menu.Item, { children: /* @__PURE__ */ jsxs(
|
|
134
|
+
"button",
|
|
135
|
+
{
|
|
136
|
+
type: "button",
|
|
137
|
+
onClick: () => {
|
|
138
|
+
handleCopy();
|
|
139
|
+
close();
|
|
140
|
+
},
|
|
141
|
+
children: [
|
|
142
|
+
/* @__PURE__ */ jsx(ClipboardCopy, { "aria-hidden": "true" }),
|
|
143
|
+
/* @__PURE__ */ jsx("span", { children: "Kopi\xE9r XML" })
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
) })
|
|
147
|
+
] })
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
),
|
|
152
|
+
/* @__PURE__ */ jsx(
|
|
153
|
+
"pre",
|
|
154
|
+
{
|
|
155
|
+
className: [styles.codePane, styles[size], wrap ? styles.wrap : styles.noWrap].join(" "),
|
|
156
|
+
tabIndex: 0,
|
|
157
|
+
onDoubleClick: handleDoubleClick,
|
|
158
|
+
children: /* @__PURE__ */ jsx("code", { ref: codeRef, children: viewMode === "raw" ? xml : tokens.map((token, index) => {
|
|
159
|
+
const tokenClassName = TOKEN_CLASS[token.type];
|
|
160
|
+
return tokenClassName ? /* @__PURE__ */ jsx("span", { className: tokenClassName, children: token.value }, index) : token.value;
|
|
161
|
+
}) })
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { XmlViewer };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
.viewer {
|
|
2
|
+
--xml-pane-bg: var(--color-bg-inverse);
|
|
3
|
+
--xml-pane-border: color-mix(in oklab, var(--color-fg-inverse) 12%, transparent);
|
|
4
|
+
--xml-pane-text: var(--color-fg-inverse);
|
|
5
|
+
|
|
6
|
+
--xml-tag: color-mix(in oklab, var(--color-fg-inverse) 66%, var(--color-syntax-accent) 34%);
|
|
7
|
+
--xml-attr-name: color-mix(in oklab, var(--color-fg-inverse) 50%, var(--color-syntax-accent) 50%);
|
|
8
|
+
--xml-attr-value: color-mix(in oklab, var(--color-fg-inverse) 20%, var(--color-syntax-string) 80%);
|
|
9
|
+
--xml-comment: color-mix(in oklab, var(--color-fg-inverse) 46%, transparent);
|
|
10
|
+
--xml-punctuation: color-mix(in oklab, var(--color-fg-inverse) 60%, transparent);
|
|
11
|
+
|
|
12
|
+
position: relative;
|
|
13
|
+
background: var(--xml-pane-bg);
|
|
14
|
+
border: var(--border-width-thin) solid var(--xml-pane-border);
|
|
15
|
+
border-radius: var(--border-radius-default);
|
|
16
|
+
box-shadow: var(--shadow-md);
|
|
17
|
+
font-family: var(--font-family-mono);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.viewer[data-appearance='light'] {
|
|
21
|
+
--xml-pane-bg: var(--color-bg-surface);
|
|
22
|
+
--xml-pane-border: var(--color-border-subtle);
|
|
23
|
+
--xml-pane-text: var(--color-fg-default);
|
|
24
|
+
|
|
25
|
+
--xml-tag: var(--color-link);
|
|
26
|
+
--xml-attr-name: color-mix(in oklab, var(--color-fg-default) 55%, var(--color-link) 45%);
|
|
27
|
+
--xml-attr-value: var(--color-status-success-fg);
|
|
28
|
+
--xml-comment: var(--color-fg-muted);
|
|
29
|
+
--xml-punctuation: var(--color-fg-subtle);
|
|
30
|
+
|
|
31
|
+
box-shadow: var(--shadow-xs);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.viewer.borderless {
|
|
35
|
+
border: none;
|
|
36
|
+
border-radius: 0;
|
|
37
|
+
box-shadow: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.toolbar {
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: var(--spacing-xs);
|
|
43
|
+
right: var(--spacing-xs);
|
|
44
|
+
z-index: 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.codePane {
|
|
48
|
+
margin: 0;
|
|
49
|
+
padding: var(--spacing-sm);
|
|
50
|
+
overflow: auto;
|
|
51
|
+
color: var(--xml-pane-text);
|
|
52
|
+
font-family: inherit;
|
|
53
|
+
line-height: 1.35;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.wrap {
|
|
57
|
+
white-space: pre-wrap;
|
|
58
|
+
overflow-wrap: break-word;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.noWrap {
|
|
62
|
+
white-space: pre;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.xs,
|
|
66
|
+
.sm {
|
|
67
|
+
font-size: var(--font-size-xs);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.md {
|
|
71
|
+
font-size: var(--font-size-sm);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.lg {
|
|
75
|
+
font-size: var(--font-size-base);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.tagName {
|
|
79
|
+
color: var(--xml-tag);
|
|
80
|
+
font-weight: var(--font-weight-medium);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.attrName {
|
|
84
|
+
color: var(--xml-attr-name);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.attrValue {
|
|
88
|
+
color: var(--xml-attr-value);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.comment {
|
|
92
|
+
color: var(--xml-comment);
|
|
93
|
+
font-style: italic;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.cdata {
|
|
97
|
+
color: var(--xml-attr-value);
|
|
98
|
+
opacity: 0.85;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.declaration,
|
|
102
|
+
.doctype {
|
|
103
|
+
color: var(--xml-comment);
|
|
104
|
+
font-style: italic;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.punctuation {
|
|
108
|
+
color: var(--xml-punctuation);
|
|
109
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const CHUNK_PATTERN = /(<!--[\s\S]*?-->)|(<!\[CDATA\[[\s\S]*?\]\]>)|(<!DOCTYPE[\s\S]*?>)|(<\?[\s\S]*?\?>)|(<\/?[a-zA-Z_:][\s\S]*?>)|([^<]+)/g;
|
|
4
|
+
const ATTR_PATTERN = /([^\s=/>]+)(\s*=\s*)("[^"]*"|'[^']*')/g;
|
|
5
|
+
function tokenizeTag(raw) {
|
|
6
|
+
var _a;
|
|
7
|
+
const isClosing = raw.startsWith("</");
|
|
8
|
+
const isSelfClosing = raw.endsWith("/>");
|
|
9
|
+
const openLength = isClosing ? 2 : 1;
|
|
10
|
+
const closeLength = isSelfClosing ? 2 : 1;
|
|
11
|
+
const inner = raw.slice(openLength, raw.length - closeLength);
|
|
12
|
+
const tokens = [{ type: "punctuation", value: raw.slice(0, openLength) }];
|
|
13
|
+
const nameMatch = /^[^\s/>]+/.exec(inner);
|
|
14
|
+
const name = (_a = nameMatch == null ? void 0 : nameMatch[0]) != null ? _a : "";
|
|
15
|
+
if (name) tokens.push({ type: "tagName", value: name });
|
|
16
|
+
let lastIndex = name.length;
|
|
17
|
+
ATTR_PATTERN.lastIndex = lastIndex;
|
|
18
|
+
let match;
|
|
19
|
+
while (match = ATTR_PATTERN.exec(inner)) {
|
|
20
|
+
if (match.index > lastIndex) {
|
|
21
|
+
tokens.push({ type: "punctuation", value: inner.slice(lastIndex, match.index) });
|
|
22
|
+
}
|
|
23
|
+
tokens.push({ type: "attrName", value: match[1] });
|
|
24
|
+
tokens.push({ type: "punctuation", value: match[2] });
|
|
25
|
+
tokens.push({ type: "attrValue", value: match[3] });
|
|
26
|
+
lastIndex = match.index + match[0].length;
|
|
27
|
+
}
|
|
28
|
+
if (lastIndex < inner.length) {
|
|
29
|
+
tokens.push({ type: "punctuation", value: inner.slice(lastIndex) });
|
|
30
|
+
}
|
|
31
|
+
tokens.push({ type: "punctuation", value: raw.slice(raw.length - closeLength) });
|
|
32
|
+
return tokens;
|
|
33
|
+
}
|
|
34
|
+
function tokenizeXml(source) {
|
|
35
|
+
const tokens = [];
|
|
36
|
+
CHUNK_PATTERN.lastIndex = 0;
|
|
37
|
+
let match;
|
|
38
|
+
while (match = CHUNK_PATTERN.exec(source)) {
|
|
39
|
+
const [, comment, cdata, doctype, declaration, tag, text] = match;
|
|
40
|
+
if (comment) tokens.push({ type: "comment", value: comment });
|
|
41
|
+
else if (cdata) tokens.push({ type: "cdata", value: cdata });
|
|
42
|
+
else if (doctype) tokens.push({ type: "doctype", value: doctype });
|
|
43
|
+
else if (declaration) tokens.push({ type: "declaration", value: declaration });
|
|
44
|
+
else if (tag) tokens.push(...tokenizeTag(tag));
|
|
45
|
+
else if (text) tokens.push({ type: "text", value: text });
|
|
46
|
+
}
|
|
47
|
+
return tokens;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
exports.tokenizeXml = tokenizeXml;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type XmlTokenType = 'tagName' | 'attrName' | 'attrValue' | 'text' | 'comment' | 'cdata' | 'declaration' | 'doctype' | 'punctuation';
|
|
2
|
+
export interface XmlToken {
|
|
3
|
+
type: XmlTokenType;
|
|
4
|
+
value: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Tokenizes an XML string for syntax highlighting without parsing it into a
|
|
8
|
+
* DOM. Preserves the source exactly - concatenating every token's `value`
|
|
9
|
+
* reproduces the input - so malformed or partial XML still highlights
|
|
10
|
+
* whatever it recognizes instead of failing outright.
|
|
11
|
+
*/
|
|
12
|
+
export declare function tokenizeXml(source: string): XmlToken[];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const CHUNK_PATTERN = /(<!--[\s\S]*?-->)|(<!\[CDATA\[[\s\S]*?\]\]>)|(<!DOCTYPE[\s\S]*?>)|(<\?[\s\S]*?\?>)|(<\/?[a-zA-Z_:][\s\S]*?>)|([^<]+)/g;
|
|
2
|
+
const ATTR_PATTERN = /([^\s=/>]+)(\s*=\s*)("[^"]*"|'[^']*')/g;
|
|
3
|
+
function tokenizeTag(raw) {
|
|
4
|
+
var _a;
|
|
5
|
+
const isClosing = raw.startsWith("</");
|
|
6
|
+
const isSelfClosing = raw.endsWith("/>");
|
|
7
|
+
const openLength = isClosing ? 2 : 1;
|
|
8
|
+
const closeLength = isSelfClosing ? 2 : 1;
|
|
9
|
+
const inner = raw.slice(openLength, raw.length - closeLength);
|
|
10
|
+
const tokens = [{ type: "punctuation", value: raw.slice(0, openLength) }];
|
|
11
|
+
const nameMatch = /^[^\s/>]+/.exec(inner);
|
|
12
|
+
const name = (_a = nameMatch == null ? void 0 : nameMatch[0]) != null ? _a : "";
|
|
13
|
+
if (name) tokens.push({ type: "tagName", value: name });
|
|
14
|
+
let lastIndex = name.length;
|
|
15
|
+
ATTR_PATTERN.lastIndex = lastIndex;
|
|
16
|
+
let match;
|
|
17
|
+
while (match = ATTR_PATTERN.exec(inner)) {
|
|
18
|
+
if (match.index > lastIndex) {
|
|
19
|
+
tokens.push({ type: "punctuation", value: inner.slice(lastIndex, match.index) });
|
|
20
|
+
}
|
|
21
|
+
tokens.push({ type: "attrName", value: match[1] });
|
|
22
|
+
tokens.push({ type: "punctuation", value: match[2] });
|
|
23
|
+
tokens.push({ type: "attrValue", value: match[3] });
|
|
24
|
+
lastIndex = match.index + match[0].length;
|
|
25
|
+
}
|
|
26
|
+
if (lastIndex < inner.length) {
|
|
27
|
+
tokens.push({ type: "punctuation", value: inner.slice(lastIndex) });
|
|
28
|
+
}
|
|
29
|
+
tokens.push({ type: "punctuation", value: raw.slice(raw.length - closeLength) });
|
|
30
|
+
return tokens;
|
|
31
|
+
}
|
|
32
|
+
function tokenizeXml(source) {
|
|
33
|
+
const tokens = [];
|
|
34
|
+
CHUNK_PATTERN.lastIndex = 0;
|
|
35
|
+
let match;
|
|
36
|
+
while (match = CHUNK_PATTERN.exec(source)) {
|
|
37
|
+
const [, comment, cdata, doctype, declaration, tag, text] = match;
|
|
38
|
+
if (comment) tokens.push({ type: "comment", value: comment });
|
|
39
|
+
else if (cdata) tokens.push({ type: "cdata", value: cdata });
|
|
40
|
+
else if (doctype) tokens.push({ type: "doctype", value: doctype });
|
|
41
|
+
else if (declaration) tokens.push({ type: "declaration", value: declaration });
|
|
42
|
+
else if (tag) tokens.push(...tokenizeTag(tag));
|
|
43
|
+
else if (text) tokens.push({ type: "text", value: text });
|
|
44
|
+
}
|
|
45
|
+
return tokens;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { tokenizeXml };
|
package/dist/index.cjs
CHANGED
|
@@ -43,6 +43,7 @@ var MediaCard = require('./components/media-card/MediaCard');
|
|
|
43
43
|
var HighlightedText = require('./components/highlighted-text/HighlightedText');
|
|
44
44
|
var ButtonSelect = require('./components/button-select/ButtonSelect');
|
|
45
45
|
var Sidebar = require('./components/sidebar/Sidebar');
|
|
46
|
+
var TreeView = require('./components/tree-view/TreeView');
|
|
46
47
|
var SplitButton = require('./components/split-button/SplitButton');
|
|
47
48
|
var ChipMultiToggle = require('./components/filtering/chip-multi-toggle/ChipMultiToggle');
|
|
48
49
|
var CheckboxGroup = require('./components/forms/checkbox-group/CheckboxGroup');
|
|
@@ -307,6 +308,12 @@ Object.keys(Sidebar).forEach(function (k) {
|
|
|
307
308
|
get: function () { return Sidebar[k]; }
|
|
308
309
|
});
|
|
309
310
|
});
|
|
311
|
+
Object.keys(TreeView).forEach(function (k) {
|
|
312
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
313
|
+
enumerable: true,
|
|
314
|
+
get: function () { return TreeView[k]; }
|
|
315
|
+
});
|
|
316
|
+
});
|
|
310
317
|
Object.keys(SplitButton).forEach(function (k) {
|
|
311
318
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
312
319
|
enumerable: true,
|
package/dist/index.css
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
@import './components/inline-status/InlineStatus.module.css';
|
|
40
40
|
@import './components/json-viewer/JsonViewer.module.css';
|
|
41
41
|
@import './components/lightbox/Lightbox.module.css';
|
|
42
|
+
@import './components/line-viewer/LineViewer.module.css';
|
|
42
43
|
@import './components/media-card/MediaCard.module.css';
|
|
43
44
|
@import './components/menu/Menu.module.css';
|
|
44
45
|
@import './components/meta-bar/MetaBar.module.css';
|
|
@@ -72,4 +73,6 @@
|
|
|
72
73
|
@import './components/table/components/empty-state/EmptyState.module.css';
|
|
73
74
|
@import './components/tabs/Tabs.module.css';
|
|
74
75
|
@import './components/toast/Toast.module.css';
|
|
76
|
+
@import './components/tree-view/TreeView.module.css';
|
|
75
77
|
@import './components/user-display/UserDisplay.module.css';
|
|
78
|
+
@import './components/xml-viewer/XmlViewer.module.css';
|
package/dist/index.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export * from './components/media-card/MediaCard';
|
|
|
42
42
|
export * from './components/highlighted-text/HighlightedText';
|
|
43
43
|
export * from './components/button-select/ButtonSelect';
|
|
44
44
|
export * from './components/sidebar/Sidebar';
|
|
45
|
+
export * from './components/tree-view/TreeView';
|
|
45
46
|
export * from './components/split-button/SplitButton';
|
|
46
47
|
export * from './components/filtering/chip-multi-toggle/ChipMultiToggle';
|
|
47
48
|
export * from './components/forms/checkbox-group/CheckboxGroup';
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,7 @@ export * from './components/media-card/MediaCard';
|
|
|
41
41
|
export * from './components/highlighted-text/HighlightedText';
|
|
42
42
|
export * from './components/button-select/ButtonSelect';
|
|
43
43
|
export * from './components/sidebar/Sidebar';
|
|
44
|
+
export * from './components/tree-view/TreeView';
|
|
44
45
|
export * from './components/split-button/SplitButton';
|
|
45
46
|
export * from './components/filtering/chip-multi-toggle/ChipMultiToggle';
|
|
46
47
|
export * from './components/forms/checkbox-group/CheckboxGroup';
|
|
@@ -9,41 +9,53 @@ function formatDate(date, options = {}) {
|
|
|
9
9
|
userFriendlyLabel = false,
|
|
10
10
|
timeOnly = false,
|
|
11
11
|
ignoreDate = false,
|
|
12
|
-
ignoreDateIfSame = false
|
|
12
|
+
ignoreDateIfSame = false,
|
|
13
|
+
ignoreTimezone = false
|
|
13
14
|
} = options;
|
|
14
15
|
const useTimeOnly = timeOnly || ignoreDate;
|
|
15
16
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const parts = (dt, useUTC) => ({
|
|
18
|
+
date: useUTC ? dt.getUTCDate() : dt.getDate(),
|
|
19
|
+
month: useUTC ? dt.getUTCMonth() : dt.getMonth(),
|
|
20
|
+
year: useUTC ? dt.getUTCFullYear() : dt.getFullYear(),
|
|
21
|
+
hours: useUTC ? dt.getUTCHours() : dt.getHours(),
|
|
22
|
+
minutes: useUTC ? dt.getUTCMinutes() : dt.getMinutes(),
|
|
23
|
+
seconds: useUTC ? dt.getUTCSeconds() : dt.getSeconds()
|
|
24
|
+
});
|
|
25
|
+
const dParts = parts(d, ignoreTimezone);
|
|
26
|
+
const formatTime = (p) => {
|
|
27
|
+
const hours = pad(p.hours);
|
|
28
|
+
const minutes = pad(p.minutes);
|
|
19
29
|
if (!showSeconds) return `${hours}:${minutes}`;
|
|
20
|
-
const seconds = pad(
|
|
30
|
+
const seconds = pad(p.seconds);
|
|
21
31
|
return `${hours}:${minutes}:${seconds}`;
|
|
22
32
|
};
|
|
23
|
-
const isSameCalendarDay = (a, b) => a.
|
|
33
|
+
const isSameCalendarDay = (a, b) => a.date === b.date && a.month === b.month && a.year === b.year;
|
|
24
34
|
if (useTimeOnly) {
|
|
25
|
-
return formatTime();
|
|
35
|
+
return formatTime(dParts);
|
|
26
36
|
}
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
const nowParts = parts(/* @__PURE__ */ new Date(), false);
|
|
38
|
+
if (ignoreDateIfSame && isSameCalendarDay(dParts, nowParts)) {
|
|
39
|
+
return formatTime(dParts);
|
|
29
40
|
}
|
|
30
41
|
if (userFriendlyLabel) {
|
|
31
42
|
const now = /* @__PURE__ */ new Date();
|
|
32
43
|
const yesterday = new Date(now);
|
|
33
44
|
yesterday.setDate(now.getDate() - 1);
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
const yesterdayParts = parts(yesterday, false);
|
|
46
|
+
if (isSameCalendarDay(dParts, nowParts)) {
|
|
47
|
+
return showTime ? `I dag, kl. ${formatTime(dParts)}` : "I dag";
|
|
36
48
|
}
|
|
37
|
-
if (isSameCalendarDay(
|
|
38
|
-
return showTime ? `I g\xE5r, kl. ${formatTime()}` : "I g\xE5r";
|
|
49
|
+
if (isSameCalendarDay(dParts, yesterdayParts)) {
|
|
50
|
+
return showTime ? `I g\xE5r, kl. ${formatTime(dParts)}` : "I g\xE5r";
|
|
39
51
|
}
|
|
40
52
|
}
|
|
41
|
-
const day = pad(
|
|
42
|
-
const month = pad(
|
|
43
|
-
const year =
|
|
53
|
+
const day = pad(dParts.date);
|
|
54
|
+
const month = pad(dParts.month + 1);
|
|
55
|
+
const year = dParts.year;
|
|
44
56
|
const base = `${day}.${month}.${year}`;
|
|
45
57
|
if (!showTime) return base;
|
|
46
|
-
return `${base} ${formatTime()}`;
|
|
58
|
+
return `${base} ${formatTime(dParts)}`;
|
|
47
59
|
}
|
|
48
60
|
|
|
49
61
|
exports.formatDate = formatDate;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - formatDate(date, { showTime: true }) => "10.03.2026 14:30"
|
|
9
9
|
* - formatDate(date, { timeOnly: true }) => "14:30"
|
|
10
10
|
* - formatDate(date, { showTime: true, ignoreDateIfSame: true }) => "14:30" for today, otherwise "10.03.2026 14:30"
|
|
11
|
+
* - formatDate('2026-03-10T14:30:00.000Z', { showTime: true, ignoreTimezone: true }) => "10.03.2026 14:30" regardless of the viewer's timezone
|
|
11
12
|
*
|
|
12
13
|
* @param date - The Date or date-parsable value to format
|
|
13
14
|
* @param options.showTime - If true, append time "hh:mm" or "hh:mm:ss"
|
|
@@ -16,6 +17,11 @@
|
|
|
16
17
|
* @param options.timeOnly - If true, return only the time portion
|
|
17
18
|
* @param options.ignoreDate - Alias for timeOnly
|
|
18
19
|
* @param options.ignoreDateIfSame - If true, return only the time when the date is today
|
|
20
|
+
* @param options.ignoreTimezone - If true, read the date's UTC field values verbatim instead of converting
|
|
21
|
+
* to the viewer's local timezone. Use this when `date` already carries the intended wall-clock values in
|
|
22
|
+
* its UTC fields (e.g. a "timestamp without time zone" DB column serialized with a trailing "Z") and any
|
|
23
|
+
* further timezone conversion would be wrong. "I dag"/"I går" comparisons still use the viewer's real
|
|
24
|
+
* local "today", since that part is genuinely about the viewer's current day.
|
|
19
25
|
* @returns A formatted string
|
|
20
26
|
*/
|
|
21
27
|
export declare function formatDate(date: Date | string | number, options?: {
|
|
@@ -25,4 +31,5 @@ export declare function formatDate(date: Date | string | number, options?: {
|
|
|
25
31
|
timeOnly?: boolean;
|
|
26
32
|
ignoreDate?: boolean;
|
|
27
33
|
ignoreDateIfSame?: boolean;
|
|
34
|
+
ignoreTimezone?: boolean;
|
|
28
35
|
}): string;
|
|
@@ -7,41 +7,53 @@ function formatDate(date, options = {}) {
|
|
|
7
7
|
userFriendlyLabel = false,
|
|
8
8
|
timeOnly = false,
|
|
9
9
|
ignoreDate = false,
|
|
10
|
-
ignoreDateIfSame = false
|
|
10
|
+
ignoreDateIfSame = false,
|
|
11
|
+
ignoreTimezone = false
|
|
11
12
|
} = options;
|
|
12
13
|
const useTimeOnly = timeOnly || ignoreDate;
|
|
13
14
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const parts = (dt, useUTC) => ({
|
|
16
|
+
date: useUTC ? dt.getUTCDate() : dt.getDate(),
|
|
17
|
+
month: useUTC ? dt.getUTCMonth() : dt.getMonth(),
|
|
18
|
+
year: useUTC ? dt.getUTCFullYear() : dt.getFullYear(),
|
|
19
|
+
hours: useUTC ? dt.getUTCHours() : dt.getHours(),
|
|
20
|
+
minutes: useUTC ? dt.getUTCMinutes() : dt.getMinutes(),
|
|
21
|
+
seconds: useUTC ? dt.getUTCSeconds() : dt.getSeconds()
|
|
22
|
+
});
|
|
23
|
+
const dParts = parts(d, ignoreTimezone);
|
|
24
|
+
const formatTime = (p) => {
|
|
25
|
+
const hours = pad(p.hours);
|
|
26
|
+
const minutes = pad(p.minutes);
|
|
17
27
|
if (!showSeconds) return `${hours}:${minutes}`;
|
|
18
|
-
const seconds = pad(
|
|
28
|
+
const seconds = pad(p.seconds);
|
|
19
29
|
return `${hours}:${minutes}:${seconds}`;
|
|
20
30
|
};
|
|
21
|
-
const isSameCalendarDay = (a, b) => a.
|
|
31
|
+
const isSameCalendarDay = (a, b) => a.date === b.date && a.month === b.month && a.year === b.year;
|
|
22
32
|
if (useTimeOnly) {
|
|
23
|
-
return formatTime();
|
|
33
|
+
return formatTime(dParts);
|
|
24
34
|
}
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
const nowParts = parts(/* @__PURE__ */ new Date(), false);
|
|
36
|
+
if (ignoreDateIfSame && isSameCalendarDay(dParts, nowParts)) {
|
|
37
|
+
return formatTime(dParts);
|
|
27
38
|
}
|
|
28
39
|
if (userFriendlyLabel) {
|
|
29
40
|
const now = /* @__PURE__ */ new Date();
|
|
30
41
|
const yesterday = new Date(now);
|
|
31
42
|
yesterday.setDate(now.getDate() - 1);
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
const yesterdayParts = parts(yesterday, false);
|
|
44
|
+
if (isSameCalendarDay(dParts, nowParts)) {
|
|
45
|
+
return showTime ? `I dag, kl. ${formatTime(dParts)}` : "I dag";
|
|
34
46
|
}
|
|
35
|
-
if (isSameCalendarDay(
|
|
36
|
-
return showTime ? `I g\xE5r, kl. ${formatTime()}` : "I g\xE5r";
|
|
47
|
+
if (isSameCalendarDay(dParts, yesterdayParts)) {
|
|
48
|
+
return showTime ? `I g\xE5r, kl. ${formatTime(dParts)}` : "I g\xE5r";
|
|
37
49
|
}
|
|
38
50
|
}
|
|
39
|
-
const day = pad(
|
|
40
|
-
const month = pad(
|
|
41
|
-
const year =
|
|
51
|
+
const day = pad(dParts.date);
|
|
52
|
+
const month = pad(dParts.month + 1);
|
|
53
|
+
const year = dParts.year;
|
|
42
54
|
const base = `${day}.${month}.${year}`;
|
|
43
55
|
if (!showTime) return base;
|
|
44
|
-
return `${base} ${formatTime()}`;
|
|
56
|
+
return `${base} ${formatTime(dParts)}`;
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
export { formatDate };
|