@umplabs/truncated-value 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/react.cjs +89 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +54 -0
- package/dist/react.d.ts +54 -0
- package/dist/react.js +87 -0
- package/dist/react.js.map +1 -0
- package/dist/truncated-value.iife.global.js +83 -0
- package/dist/truncated-value.iife.global.js.map +1 -0
- package/dist/web-component.cjs +116 -0
- package/dist/web-component.cjs.map +1 -0
- package/dist/web-component.d.cts +44 -0
- package/dist/web-component.d.ts +44 -0
- package/dist/web-component.js +116 -0
- package/dist/web-component.js.map +1 -0
- package/package.json +131 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var lit = require('lit');
|
|
4
|
+
var decorators_js = require('lit/decorators.js');
|
|
5
|
+
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
9
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
10
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
11
|
+
if (decorator = decorators[i])
|
|
12
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
13
|
+
if (kind && result) __defProp(target, key, result);
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/core/truncate.ts
|
|
18
|
+
function truncate(options) {
|
|
19
|
+
const { value, startLength = 6, endLength = 4 } = options;
|
|
20
|
+
if (!value) {
|
|
21
|
+
return {
|
|
22
|
+
start: "",
|
|
23
|
+
middle: "",
|
|
24
|
+
end: "",
|
|
25
|
+
isTruncated: false
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const isTruncated = startLength + endLength < value.length;
|
|
29
|
+
const excess = Math.max(0, startLength + endLength - value.length);
|
|
30
|
+
const adjustedStartLength = startLength - (startLength > endLength ? Math.ceil : Math.floor)(excess / 2);
|
|
31
|
+
const adjustedEndLength = endLength - (startLength > endLength ? Math.floor : Math.ceil)(excess / 2);
|
|
32
|
+
const safeStartLength = Math.max(
|
|
33
|
+
0,
|
|
34
|
+
Math.min(adjustedStartLength, value.length)
|
|
35
|
+
);
|
|
36
|
+
const safeEndLength = Math.max(
|
|
37
|
+
0,
|
|
38
|
+
Math.min(adjustedEndLength, value.length - safeStartLength)
|
|
39
|
+
);
|
|
40
|
+
const start = value.slice(0, safeStartLength);
|
|
41
|
+
const end = safeEndLength > 0 ? value.slice(-safeEndLength) : "";
|
|
42
|
+
const middle = value.slice(
|
|
43
|
+
safeStartLength,
|
|
44
|
+
safeEndLength > 0 ? -safeEndLength : value.length
|
|
45
|
+
);
|
|
46
|
+
return {
|
|
47
|
+
start,
|
|
48
|
+
middle,
|
|
49
|
+
end,
|
|
50
|
+
isTruncated
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/core/styles.ts
|
|
55
|
+
var styles = `
|
|
56
|
+
[tabindex].truncated-value { --isTruncated: 0; }
|
|
57
|
+
[tabindex].truncated-value:not(:is(:active, :focus-within, [data-is-truncated="false"])) { --isTruncated: 1; cursor: zoom-in; }
|
|
58
|
+
[tabindex="-1"].truncated-value:not([data-is-truncated="false"]) { --isTruncated: 1 !important; cursor: default !important; }
|
|
59
|
+
[tabindex].truncated-value > .tv-middle > span:not(:empty) { font-size: calc((1 - var(--isTruncated)) * 1em); }
|
|
60
|
+
[tabindex].truncated-value > .tv-middle > span:empty { font-size: calc(var(--isTruncated) * 1em); pointer-events: none; }
|
|
61
|
+
[tabindex].truncated-value > .tv-middle > span:empty::after { content: "..."; }
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
// src/web-component.ts
|
|
65
|
+
exports.TruncatedValueElement = class TruncatedValueElement extends lit.LitElement {
|
|
66
|
+
constructor() {
|
|
67
|
+
super(...arguments);
|
|
68
|
+
this.value = "";
|
|
69
|
+
this.startLength = 6;
|
|
70
|
+
this.endLength = 4;
|
|
71
|
+
this.expandable = false;
|
|
72
|
+
}
|
|
73
|
+
render() {
|
|
74
|
+
const { start, middle, end, isTruncated } = truncate({
|
|
75
|
+
value: this.value,
|
|
76
|
+
startLength: this.startLength,
|
|
77
|
+
endLength: this.endLength
|
|
78
|
+
});
|
|
79
|
+
const middleFirstHalf = middle.slice(0, middle.length / 2);
|
|
80
|
+
const middleSecondHalf = middle.slice(middle.length / 2);
|
|
81
|
+
return lit.html`<span
|
|
82
|
+
part="container"
|
|
83
|
+
data-is-truncated="${isTruncated}"
|
|
84
|
+
tabindex="${this.expandable ? 0 : -1}"
|
|
85
|
+
class="truncated-value"
|
|
86
|
+
><span part="start">${start}</span
|
|
87
|
+
><span part="middle" class="tv-middle"
|
|
88
|
+
><span>${middleFirstHalf}</span><span aria-hidden="true"></span
|
|
89
|
+
><span>${middleSecondHalf}</span></span
|
|
90
|
+
><span part="end">${end}</span></span
|
|
91
|
+
>`;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
exports.TruncatedValueElement.styles = lit.css`
|
|
95
|
+
:host {
|
|
96
|
+
display: inline-block;
|
|
97
|
+
}
|
|
98
|
+
${lit.unsafeCSS(styles)}
|
|
99
|
+
`;
|
|
100
|
+
__decorateClass([
|
|
101
|
+
decorators_js.property({ type: String })
|
|
102
|
+
], exports.TruncatedValueElement.prototype, "value", 2);
|
|
103
|
+
__decorateClass([
|
|
104
|
+
decorators_js.property({ type: Number, attribute: "start-length" })
|
|
105
|
+
], exports.TruncatedValueElement.prototype, "startLength", 2);
|
|
106
|
+
__decorateClass([
|
|
107
|
+
decorators_js.property({ type: Number, attribute: "end-length" })
|
|
108
|
+
], exports.TruncatedValueElement.prototype, "endLength", 2);
|
|
109
|
+
__decorateClass([
|
|
110
|
+
decorators_js.property({ type: Boolean })
|
|
111
|
+
], exports.TruncatedValueElement.prototype, "expandable", 2);
|
|
112
|
+
exports.TruncatedValueElement = __decorateClass([
|
|
113
|
+
decorators_js.customElement("truncated-value")
|
|
114
|
+
], exports.TruncatedValueElement);
|
|
115
|
+
//# sourceMappingURL=web-component.cjs.map
|
|
116
|
+
//# sourceMappingURL=web-component.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/truncate.ts","../src/core/styles.ts","../src/web-component.ts"],"names":["TruncatedValueElement","LitElement","html","css","unsafeCSS","property","customElement"],"mappings":"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","file":"web-component.cjs","sourcesContent":["import type { TruncateOptions, TruncateResult } from \"./types\";\n\n/**\n * Truncates a string value intelligently, preserving the start and end portions.\n *\n * The function handles edge cases like:\n * - Strings shorter than the requested lengths (adjusts proportionally)\n * - Zero or very large length values\n * - Empty strings\n *\n * @example\n * ```ts\n * const result = truncate({\n * value: \"0x1234567890abcdef1234567890abcdef12345678\",\n * startLength: 6,\n * endLength: 4\n * });\n * // result.start = \"0x1234\"\n * // result.middle = \"567890abcdef1234567890abcdef1234\"\n * // result.end = \"5678\"\n * // result.isTruncated = true\n * ```\n */\nexport function truncate(options: TruncateOptions): TruncateResult {\n const { value, startLength = 6, endLength = 4 } = options;\n\n // Handle empty or undefined values\n if (!value) {\n return {\n start: \"\",\n middle: \"\",\n end: \"\",\n isTruncated: false,\n };\n }\n\n // Determine if truncation is needed\n const isTruncated = startLength + endLength < value.length;\n\n // Calculate excess characters when string is shorter than requested lengths\n const excess = Math.max(0, startLength + endLength - value.length);\n\n // Adjust lengths proportionally when string is too short\n const adjustedStartLength =\n startLength -\n (startLength > endLength ? Math.ceil : Math.floor)(excess / 2);\n const adjustedEndLength =\n endLength - (startLength > endLength ? Math.floor : Math.ceil)(excess / 2);\n\n // Ensure we don't get negative lengths\n const safeStartLength = Math.max(\n 0,\n Math.min(adjustedStartLength, value.length)\n );\n const safeEndLength = Math.max(\n 0,\n Math.min(adjustedEndLength, value.length - safeStartLength)\n );\n\n // Split the value into start, middle, and end segments\n const start = value.slice(0, safeStartLength);\n const end = safeEndLength > 0 ? value.slice(-safeEndLength) : \"\";\n const middle = value.slice(\n safeStartLength,\n safeEndLength > 0 ? -safeEndLength : value.length\n );\n\n return {\n start,\n middle,\n end,\n isTruncated,\n };\n}\n","/**\n * CSS styles for the TruncatedValue component.\n *\n * How it works:\n * - `--isTruncated` CSS variable (0=expanded, 1=truncated) represents state\n * - Visual default is truncated; expands on :active or :focus-within\n * - Short values (data-is-truncated=\"false\") stay expanded\n * - Non-expandable (tabindex=\"-1\") stays truncated unless value is short\n * - Middle text: font-size set to 0 when truncated (hidden but searchable)\n * - Ellipsis: empty span with ::after content, font-size set inverse\n */\nexport const styles = `\n[tabindex].truncated-value { --isTruncated: 0; }\n[tabindex].truncated-value:not(:is(:active, :focus-within, [data-is-truncated=\"false\"])) { --isTruncated: 1; cursor: zoom-in; }\n[tabindex=\"-1\"].truncated-value:not([data-is-truncated=\"false\"]) { --isTruncated: 1 !important; cursor: default !important; }\n[tabindex].truncated-value > .tv-middle > span:not(:empty) { font-size: calc((1 - var(--isTruncated)) * 1em); }\n[tabindex].truncated-value > .tv-middle > span:empty { font-size: calc(var(--isTruncated) * 1em); pointer-events: none; }\n[tabindex].truncated-value > .tv-middle > span:empty::after { content: \"...\"; }\n`;\n","import { LitElement, html, css, unsafeCSS } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { truncate, styles } from \"./core\";\n\n/**\n * A web component that intelligently truncates long string values.\n *\n * @element truncated-value\n *\n * @attr {string} value - The string value to display and potentially truncate\n * @attr {number} start-length - Number of characters to show at the start (default: 6)\n * @attr {number} end-length - Number of characters to show at the end (default: 4)\n * @attr {boolean} expandable - Whether the component should expand on focus/click\n *\n * @csspart container - The main container element\n * @csspart start - The start portion text\n * @csspart middle - The middle portion container\n * @csspart end - The end portion text\n *\n *\n * @example\n * ```html\n * <truncated-value\n * value=\"0x1234567890abcdef1234567890abcdef12345678\"\n * start-length=\"8\"\n * end-length=\"6\"\n * expandable\n * ></truncated-value>\n * ```\n */\n@customElement(\"truncated-value\")\nexport class TruncatedValueElement extends LitElement {\n static override styles = css`\n :host {\n display: inline-block;\n }\n ${unsafeCSS(styles)}\n `;\n\n @property({ type: String })\n value = \"\";\n\n @property({ type: Number, attribute: \"start-length\" })\n startLength = 6;\n\n @property({ type: Number, attribute: \"end-length\" })\n endLength = 4;\n\n @property({ type: Boolean })\n expandable = false;\n\n override render() {\n const { start, middle, end, isTruncated } = truncate({\n value: this.value,\n startLength: this.startLength,\n endLength: this.endLength,\n });\n\n const middleFirstHalf = middle.slice(0, middle.length / 2);\n const middleSecondHalf = middle.slice(middle.length / 2);\n\n return html`<span\n part=\"container\"\n data-is-truncated=\"${isTruncated}\"\n tabindex=\"${this.expandable ? 0 : -1}\"\n class=\"truncated-value\"\n ><span part=\"start\">${start}</span\n ><span part=\"middle\" class=\"tv-middle\"\n ><span>${middleFirstHalf}</span><span aria-hidden=\"true\"></span\n ><span>${middleSecondHalf}</span></span\n ><span part=\"end\">${end}</span></span\n >`;\n }\n}\n\n// Declare for type checking\ndeclare global {\n interface HTMLElementTagNameMap {\n \"truncated-value\": TruncatedValueElement;\n }\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as lit from 'lit';
|
|
2
|
+
import { LitElement } from 'lit';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A web component that intelligently truncates long string values.
|
|
6
|
+
*
|
|
7
|
+
* @element truncated-value
|
|
8
|
+
*
|
|
9
|
+
* @attr {string} value - The string value to display and potentially truncate
|
|
10
|
+
* @attr {number} start-length - Number of characters to show at the start (default: 6)
|
|
11
|
+
* @attr {number} end-length - Number of characters to show at the end (default: 4)
|
|
12
|
+
* @attr {boolean} expandable - Whether the component should expand on focus/click
|
|
13
|
+
*
|
|
14
|
+
* @csspart container - The main container element
|
|
15
|
+
* @csspart start - The start portion text
|
|
16
|
+
* @csspart middle - The middle portion container
|
|
17
|
+
* @csspart end - The end portion text
|
|
18
|
+
*
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```html
|
|
22
|
+
* <truncated-value
|
|
23
|
+
* value="0x1234567890abcdef1234567890abcdef12345678"
|
|
24
|
+
* start-length="8"
|
|
25
|
+
* end-length="6"
|
|
26
|
+
* expandable
|
|
27
|
+
* ></truncated-value>
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
declare class TruncatedValueElement extends LitElement {
|
|
31
|
+
static styles: lit.CSSResult;
|
|
32
|
+
value: string;
|
|
33
|
+
startLength: number;
|
|
34
|
+
endLength: number;
|
|
35
|
+
expandable: boolean;
|
|
36
|
+
render(): lit.TemplateResult<1>;
|
|
37
|
+
}
|
|
38
|
+
declare global {
|
|
39
|
+
interface HTMLElementTagNameMap {
|
|
40
|
+
"truncated-value": TruncatedValueElement;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { TruncatedValueElement };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as lit from 'lit';
|
|
2
|
+
import { LitElement } from 'lit';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A web component that intelligently truncates long string values.
|
|
6
|
+
*
|
|
7
|
+
* @element truncated-value
|
|
8
|
+
*
|
|
9
|
+
* @attr {string} value - The string value to display and potentially truncate
|
|
10
|
+
* @attr {number} start-length - Number of characters to show at the start (default: 6)
|
|
11
|
+
* @attr {number} end-length - Number of characters to show at the end (default: 4)
|
|
12
|
+
* @attr {boolean} expandable - Whether the component should expand on focus/click
|
|
13
|
+
*
|
|
14
|
+
* @csspart container - The main container element
|
|
15
|
+
* @csspart start - The start portion text
|
|
16
|
+
* @csspart middle - The middle portion container
|
|
17
|
+
* @csspart end - The end portion text
|
|
18
|
+
*
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```html
|
|
22
|
+
* <truncated-value
|
|
23
|
+
* value="0x1234567890abcdef1234567890abcdef12345678"
|
|
24
|
+
* start-length="8"
|
|
25
|
+
* end-length="6"
|
|
26
|
+
* expandable
|
|
27
|
+
* ></truncated-value>
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
declare class TruncatedValueElement extends LitElement {
|
|
31
|
+
static styles: lit.CSSResult;
|
|
32
|
+
value: string;
|
|
33
|
+
startLength: number;
|
|
34
|
+
endLength: number;
|
|
35
|
+
expandable: boolean;
|
|
36
|
+
render(): lit.TemplateResult<1>;
|
|
37
|
+
}
|
|
38
|
+
declare global {
|
|
39
|
+
interface HTMLElementTagNameMap {
|
|
40
|
+
"truncated-value": TruncatedValueElement;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { TruncatedValueElement };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { unsafeCSS, css, LitElement, html } from 'lit';
|
|
2
|
+
import { property, customElement } from 'lit/decorators.js';
|
|
3
|
+
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
7
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
8
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
9
|
+
if (decorator = decorators[i])
|
|
10
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
11
|
+
if (kind && result) __defProp(target, key, result);
|
|
12
|
+
return result;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/core/truncate.ts
|
|
16
|
+
function truncate(options) {
|
|
17
|
+
const { value, startLength = 6, endLength = 4 } = options;
|
|
18
|
+
if (!value) {
|
|
19
|
+
return {
|
|
20
|
+
start: "",
|
|
21
|
+
middle: "",
|
|
22
|
+
end: "",
|
|
23
|
+
isTruncated: false
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const isTruncated = startLength + endLength < value.length;
|
|
27
|
+
const excess = Math.max(0, startLength + endLength - value.length);
|
|
28
|
+
const adjustedStartLength = startLength - (startLength > endLength ? Math.ceil : Math.floor)(excess / 2);
|
|
29
|
+
const adjustedEndLength = endLength - (startLength > endLength ? Math.floor : Math.ceil)(excess / 2);
|
|
30
|
+
const safeStartLength = Math.max(
|
|
31
|
+
0,
|
|
32
|
+
Math.min(adjustedStartLength, value.length)
|
|
33
|
+
);
|
|
34
|
+
const safeEndLength = Math.max(
|
|
35
|
+
0,
|
|
36
|
+
Math.min(adjustedEndLength, value.length - safeStartLength)
|
|
37
|
+
);
|
|
38
|
+
const start = value.slice(0, safeStartLength);
|
|
39
|
+
const end = safeEndLength > 0 ? value.slice(-safeEndLength) : "";
|
|
40
|
+
const middle = value.slice(
|
|
41
|
+
safeStartLength,
|
|
42
|
+
safeEndLength > 0 ? -safeEndLength : value.length
|
|
43
|
+
);
|
|
44
|
+
return {
|
|
45
|
+
start,
|
|
46
|
+
middle,
|
|
47
|
+
end,
|
|
48
|
+
isTruncated
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/core/styles.ts
|
|
53
|
+
var styles = `
|
|
54
|
+
[tabindex].truncated-value { --isTruncated: 0; }
|
|
55
|
+
[tabindex].truncated-value:not(:is(:active, :focus-within, [data-is-truncated="false"])) { --isTruncated: 1; cursor: zoom-in; }
|
|
56
|
+
[tabindex="-1"].truncated-value:not([data-is-truncated="false"]) { --isTruncated: 1 !important; cursor: default !important; }
|
|
57
|
+
[tabindex].truncated-value > .tv-middle > span:not(:empty) { font-size: calc((1 - var(--isTruncated)) * 1em); }
|
|
58
|
+
[tabindex].truncated-value > .tv-middle > span:empty { font-size: calc(var(--isTruncated) * 1em); pointer-events: none; }
|
|
59
|
+
[tabindex].truncated-value > .tv-middle > span:empty::after { content: "..."; }
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
// src/web-component.ts
|
|
63
|
+
var TruncatedValueElement = class extends LitElement {
|
|
64
|
+
constructor() {
|
|
65
|
+
super(...arguments);
|
|
66
|
+
this.value = "";
|
|
67
|
+
this.startLength = 6;
|
|
68
|
+
this.endLength = 4;
|
|
69
|
+
this.expandable = false;
|
|
70
|
+
}
|
|
71
|
+
render() {
|
|
72
|
+
const { start, middle, end, isTruncated } = truncate({
|
|
73
|
+
value: this.value,
|
|
74
|
+
startLength: this.startLength,
|
|
75
|
+
endLength: this.endLength
|
|
76
|
+
});
|
|
77
|
+
const middleFirstHalf = middle.slice(0, middle.length / 2);
|
|
78
|
+
const middleSecondHalf = middle.slice(middle.length / 2);
|
|
79
|
+
return html`<span
|
|
80
|
+
part="container"
|
|
81
|
+
data-is-truncated="${isTruncated}"
|
|
82
|
+
tabindex="${this.expandable ? 0 : -1}"
|
|
83
|
+
class="truncated-value"
|
|
84
|
+
><span part="start">${start}</span
|
|
85
|
+
><span part="middle" class="tv-middle"
|
|
86
|
+
><span>${middleFirstHalf}</span><span aria-hidden="true"></span
|
|
87
|
+
><span>${middleSecondHalf}</span></span
|
|
88
|
+
><span part="end">${end}</span></span
|
|
89
|
+
>`;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
TruncatedValueElement.styles = css`
|
|
93
|
+
:host {
|
|
94
|
+
display: inline-block;
|
|
95
|
+
}
|
|
96
|
+
${unsafeCSS(styles)}
|
|
97
|
+
`;
|
|
98
|
+
__decorateClass([
|
|
99
|
+
property({ type: String })
|
|
100
|
+
], TruncatedValueElement.prototype, "value", 2);
|
|
101
|
+
__decorateClass([
|
|
102
|
+
property({ type: Number, attribute: "start-length" })
|
|
103
|
+
], TruncatedValueElement.prototype, "startLength", 2);
|
|
104
|
+
__decorateClass([
|
|
105
|
+
property({ type: Number, attribute: "end-length" })
|
|
106
|
+
], TruncatedValueElement.prototype, "endLength", 2);
|
|
107
|
+
__decorateClass([
|
|
108
|
+
property({ type: Boolean })
|
|
109
|
+
], TruncatedValueElement.prototype, "expandable", 2);
|
|
110
|
+
TruncatedValueElement = __decorateClass([
|
|
111
|
+
customElement("truncated-value")
|
|
112
|
+
], TruncatedValueElement);
|
|
113
|
+
|
|
114
|
+
export { TruncatedValueElement };
|
|
115
|
+
//# sourceMappingURL=web-component.js.map
|
|
116
|
+
//# sourceMappingURL=web-component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/truncate.ts","../src/core/styles.ts","../src/web-component.ts"],"names":[],"mappings":"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","file":"web-component.js","sourcesContent":["import type { TruncateOptions, TruncateResult } from \"./types\";\n\n/**\n * Truncates a string value intelligently, preserving the start and end portions.\n *\n * The function handles edge cases like:\n * - Strings shorter than the requested lengths (adjusts proportionally)\n * - Zero or very large length values\n * - Empty strings\n *\n * @example\n * ```ts\n * const result = truncate({\n * value: \"0x1234567890abcdef1234567890abcdef12345678\",\n * startLength: 6,\n * endLength: 4\n * });\n * // result.start = \"0x1234\"\n * // result.middle = \"567890abcdef1234567890abcdef1234\"\n * // result.end = \"5678\"\n * // result.isTruncated = true\n * ```\n */\nexport function truncate(options: TruncateOptions): TruncateResult {\n const { value, startLength = 6, endLength = 4 } = options;\n\n // Handle empty or undefined values\n if (!value) {\n return {\n start: \"\",\n middle: \"\",\n end: \"\",\n isTruncated: false,\n };\n }\n\n // Determine if truncation is needed\n const isTruncated = startLength + endLength < value.length;\n\n // Calculate excess characters when string is shorter than requested lengths\n const excess = Math.max(0, startLength + endLength - value.length);\n\n // Adjust lengths proportionally when string is too short\n const adjustedStartLength =\n startLength -\n (startLength > endLength ? Math.ceil : Math.floor)(excess / 2);\n const adjustedEndLength =\n endLength - (startLength > endLength ? Math.floor : Math.ceil)(excess / 2);\n\n // Ensure we don't get negative lengths\n const safeStartLength = Math.max(\n 0,\n Math.min(adjustedStartLength, value.length)\n );\n const safeEndLength = Math.max(\n 0,\n Math.min(adjustedEndLength, value.length - safeStartLength)\n );\n\n // Split the value into start, middle, and end segments\n const start = value.slice(0, safeStartLength);\n const end = safeEndLength > 0 ? value.slice(-safeEndLength) : \"\";\n const middle = value.slice(\n safeStartLength,\n safeEndLength > 0 ? -safeEndLength : value.length\n );\n\n return {\n start,\n middle,\n end,\n isTruncated,\n };\n}\n","/**\n * CSS styles for the TruncatedValue component.\n *\n * How it works:\n * - `--isTruncated` CSS variable (0=expanded, 1=truncated) represents state\n * - Visual default is truncated; expands on :active or :focus-within\n * - Short values (data-is-truncated=\"false\") stay expanded\n * - Non-expandable (tabindex=\"-1\") stays truncated unless value is short\n * - Middle text: font-size set to 0 when truncated (hidden but searchable)\n * - Ellipsis: empty span with ::after content, font-size set inverse\n */\nexport const styles = `\n[tabindex].truncated-value { --isTruncated: 0; }\n[tabindex].truncated-value:not(:is(:active, :focus-within, [data-is-truncated=\"false\"])) { --isTruncated: 1; cursor: zoom-in; }\n[tabindex=\"-1\"].truncated-value:not([data-is-truncated=\"false\"]) { --isTruncated: 1 !important; cursor: default !important; }\n[tabindex].truncated-value > .tv-middle > span:not(:empty) { font-size: calc((1 - var(--isTruncated)) * 1em); }\n[tabindex].truncated-value > .tv-middle > span:empty { font-size: calc(var(--isTruncated) * 1em); pointer-events: none; }\n[tabindex].truncated-value > .tv-middle > span:empty::after { content: \"...\"; }\n`;\n","import { LitElement, html, css, unsafeCSS } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { truncate, styles } from \"./core\";\n\n/**\n * A web component that intelligently truncates long string values.\n *\n * @element truncated-value\n *\n * @attr {string} value - The string value to display and potentially truncate\n * @attr {number} start-length - Number of characters to show at the start (default: 6)\n * @attr {number} end-length - Number of characters to show at the end (default: 4)\n * @attr {boolean} expandable - Whether the component should expand on focus/click\n *\n * @csspart container - The main container element\n * @csspart start - The start portion text\n * @csspart middle - The middle portion container\n * @csspart end - The end portion text\n *\n *\n * @example\n * ```html\n * <truncated-value\n * value=\"0x1234567890abcdef1234567890abcdef12345678\"\n * start-length=\"8\"\n * end-length=\"6\"\n * expandable\n * ></truncated-value>\n * ```\n */\n@customElement(\"truncated-value\")\nexport class TruncatedValueElement extends LitElement {\n static override styles = css`\n :host {\n display: inline-block;\n }\n ${unsafeCSS(styles)}\n `;\n\n @property({ type: String })\n value = \"\";\n\n @property({ type: Number, attribute: \"start-length\" })\n startLength = 6;\n\n @property({ type: Number, attribute: \"end-length\" })\n endLength = 4;\n\n @property({ type: Boolean })\n expandable = false;\n\n override render() {\n const { start, middle, end, isTruncated } = truncate({\n value: this.value,\n startLength: this.startLength,\n endLength: this.endLength,\n });\n\n const middleFirstHalf = middle.slice(0, middle.length / 2);\n const middleSecondHalf = middle.slice(middle.length / 2);\n\n return html`<span\n part=\"container\"\n data-is-truncated=\"${isTruncated}\"\n tabindex=\"${this.expandable ? 0 : -1}\"\n class=\"truncated-value\"\n ><span part=\"start\">${start}</span\n ><span part=\"middle\" class=\"tv-middle\"\n ><span>${middleFirstHalf}</span><span aria-hidden=\"true\"></span\n ><span>${middleSecondHalf}</span></span\n ><span part=\"end\">${end}</span></span\n >`;\n }\n}\n\n// Declare for type checking\ndeclare global {\n interface HTMLElementTagNameMap {\n \"truncated-value\": TruncatedValueElement;\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@umplabs/truncated-value",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A component that intelligently truncates long string values with interactive expansion and full searchability",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"truncate",
|
|
7
|
+
"ellipsis",
|
|
8
|
+
"address",
|
|
9
|
+
"ethereum",
|
|
10
|
+
"web3",
|
|
11
|
+
"react",
|
|
12
|
+
"web-component",
|
|
13
|
+
"lit"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "UMP Labs",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/umpeth/truncated-value.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/umpeth/truncated-value/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/umpeth/truncated-value#readme",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"exports": {
|
|
27
|
+
"./react": {
|
|
28
|
+
"import": {
|
|
29
|
+
"types": "./dist/react.d.ts",
|
|
30
|
+
"default": "./dist/react.js"
|
|
31
|
+
},
|
|
32
|
+
"require": {
|
|
33
|
+
"types": "./dist/react.d.cts",
|
|
34
|
+
"default": "./dist/react.cjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"./web-component": {
|
|
38
|
+
"import": {
|
|
39
|
+
"types": "./dist/web-component.d.ts",
|
|
40
|
+
"default": "./dist/web-component.js"
|
|
41
|
+
},
|
|
42
|
+
"require": {
|
|
43
|
+
"types": "./dist/web-component.d.cts",
|
|
44
|
+
"default": "./dist/web-component.cjs"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"README.md",
|
|
51
|
+
"LICENSE"
|
|
52
|
+
],
|
|
53
|
+
"sideEffects": [
|
|
54
|
+
"./dist/web-component.js",
|
|
55
|
+
"./dist/web-component.cjs"
|
|
56
|
+
],
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"lit": "^3.2.0",
|
|
59
|
+
"react": ">=17",
|
|
60
|
+
"react-dom": ">=17"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"lit": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"react": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"react-dom": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@eslint/js": "^9.39.2",
|
|
75
|
+
"@playwright/test": "^1.58.1",
|
|
76
|
+
"@size-limit/file": "^12.0.0",
|
|
77
|
+
"@storybook/react-vite": "^10.2.2",
|
|
78
|
+
"@testing-library/dom": "^10.4.0",
|
|
79
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
80
|
+
"@testing-library/react": "^16.0.0",
|
|
81
|
+
"@types/node": "^25.0.0",
|
|
82
|
+
"@types/react": "^19.0.0",
|
|
83
|
+
"@types/react-dom": "^19.0.0",
|
|
84
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
85
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
86
|
+
"@vitest/coverage-v8": "^4.0.0",
|
|
87
|
+
"eslint": "^9.0.0",
|
|
88
|
+
"eslint-plugin-react": "^7.35.0",
|
|
89
|
+
"eslint-plugin-react-hooks": "^7.0.0",
|
|
90
|
+
"happy-dom": "^20.4.0",
|
|
91
|
+
"husky": "^9.1.7",
|
|
92
|
+
"lint-staged": "^16.2.7",
|
|
93
|
+
"prettier": "^3.8.1",
|
|
94
|
+
"react": "^19.0.0",
|
|
95
|
+
"react-dom": "^19.0.0",
|
|
96
|
+
"serve": "^14.2.5",
|
|
97
|
+
"size-limit": "^12.0.0",
|
|
98
|
+
"storybook": "^10.2.2",
|
|
99
|
+
"tsup": "^8.3.0",
|
|
100
|
+
"typescript": "^5.6.0",
|
|
101
|
+
"typescript-eslint": "^8.54.0",
|
|
102
|
+
"vitest": "^4.0.0"
|
|
103
|
+
},
|
|
104
|
+
"engines": {
|
|
105
|
+
"node": ">=18"
|
|
106
|
+
},
|
|
107
|
+
"lint-staged": {
|
|
108
|
+
"*.{js,jsx,ts,tsx}": [
|
|
109
|
+
"eslint --fix",
|
|
110
|
+
"prettier --write"
|
|
111
|
+
],
|
|
112
|
+
"!(*.{js,jsx,ts,tsx})": [
|
|
113
|
+
"prettier --write --ignore-unknown"
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
"scripts": {
|
|
117
|
+
"build": "tsup",
|
|
118
|
+
"dev": "tsup --watch",
|
|
119
|
+
"test": "vitest run",
|
|
120
|
+
"test:watch": "vitest",
|
|
121
|
+
"test:coverage": "vitest run --coverage",
|
|
122
|
+
"test:e2e": "playwright test",
|
|
123
|
+
"test:e2e:ui": "playwright test --ui",
|
|
124
|
+
"lint": "eslint src tests",
|
|
125
|
+
"typecheck": "tsc --noEmit",
|
|
126
|
+
"format": "prettier --write .",
|
|
127
|
+
"storybook": "storybook dev -p 6006",
|
|
128
|
+
"build-storybook": "storybook build",
|
|
129
|
+
"size": "size-limit"
|
|
130
|
+
}
|
|
131
|
+
}
|