trackhome-react 0.3.0 → 0.5.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/README.md +4 -2
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -158
- package/dist/index.js.map +1 -1
- package/dist/widgets/categorical.d.ts +6 -0
- package/dist/widgets/categorical.d.ts.map +1 -0
- package/dist/widgets/categorical.js +37 -0
- package/dist/widgets/categorical.js.map +1 -0
- package/dist/widgets/common.d.ts +42 -0
- package/dist/widgets/common.d.ts.map +1 -0
- package/dist/widgets/common.js +39 -0
- package/dist/widgets/common.js.map +1 -0
- package/dist/widgets/funnel.d.ts +3 -0
- package/dist/widgets/funnel.d.ts.map +1 -0
- package/dist/widgets/funnel.js +83 -0
- package/dist/widgets/funnel.js.map +1 -0
- package/dist/widgets/index.d.ts +4 -0
- package/dist/widgets/index.d.ts.map +1 -0
- package/dist/widgets/index.js +4 -0
- package/dist/widgets/index.js.map +1 -0
- package/dist/widgets/inputs.d.ts +32 -0
- package/dist/widgets/inputs.d.ts.map +1 -0
- package/dist/widgets/inputs.js +312 -0
- package/dist/widgets/inputs.js.map +1 -0
- package/dist/widgets/metric.d.ts +3 -0
- package/dist/widgets/metric.d.ts.map +1 -0
- package/dist/widgets/metric.js +14 -0
- package/dist/widgets/metric.js.map +1 -0
- package/dist/widgets/registry.d.ts +26 -0
- package/dist/widgets/registry.d.ts.map +1 -0
- package/dist/widgets/registry.js +53 -0
- package/dist/widgets/registry.js.map +1 -0
- package/dist/widgets/timeseries.d.ts +4 -0
- package/dist/widgets/timeseries.d.ts.map +1 -0
- package/dist/widgets/timeseries.js +34 -0
- package/dist/widgets/timeseries.js.map +1 -0
- package/dist/widgets/types.d.ts +87 -0
- package/dist/widgets/types.d.ts.map +1 -0
- package/dist/widgets/types.js +18 -0
- package/dist/widgets/types.js.map +1 -0
- package/package.json +2 -7
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Field, RankedBars, MetricField, EventTypeField, TagPrefixField } from './common.js';
|
|
3
|
+
/** All categorical kinds resolve to a ranked [{type|tag, count}] list. */
|
|
4
|
+
function CategoricalRender({ data, theme }) {
|
|
5
|
+
const d = data ?? [];
|
|
6
|
+
const rows = d.map((x) => ({ label: x.type ?? x.tag ?? '—', count: x.count }));
|
|
7
|
+
return _jsx(RankedBars, { rows: rows, theme: theme });
|
|
8
|
+
}
|
|
9
|
+
function BreakdownEditForm({ draft, update, ctx }) {
|
|
10
|
+
return (_jsxs(_Fragment, { children: [_jsx(Field, { label: "Breakdown", ctx: ctx, children: _jsxs("select", { style: ctx.inputStyle, value: draft.breakdown ?? 'event_type', onChange: (e) => update({ breakdown: e.target.value }), children: [_jsx("option", { value: "event_type", children: "Event type" }), _jsx("option", { value: "tag", children: "Tag value" })] }) }), draft.breakdown === 'tag' && _jsx(TagPrefixField, { draft: draft, update: update, ctx: ctx }), _jsx(MetricField, { draft: draft, update: update, ctx: ctx }), _jsx(EventTypeField, { draft: draft, update: update, ctx: ctx })] }));
|
|
11
|
+
}
|
|
12
|
+
const breakdownConfig = () => ({ breakdown: 'event_type', metric: 'count' });
|
|
13
|
+
export const barWidget = {
|
|
14
|
+
meta: { value: 'bar', label: 'Bar', hint: 'Vertical categorical bars' },
|
|
15
|
+
defaultConfig: breakdownConfig,
|
|
16
|
+
Render: CategoricalRender,
|
|
17
|
+
EditForm: BreakdownEditForm,
|
|
18
|
+
};
|
|
19
|
+
export const pieWidget = {
|
|
20
|
+
meta: { value: 'pie', label: 'Pie', hint: 'Donut share-of-total' },
|
|
21
|
+
defaultConfig: breakdownConfig,
|
|
22
|
+
Render: CategoricalRender,
|
|
23
|
+
EditForm: BreakdownEditForm,
|
|
24
|
+
};
|
|
25
|
+
export const topEventsWidget = {
|
|
26
|
+
meta: { value: 'top_events', label: 'Top events', hint: 'Ranked event-type list' },
|
|
27
|
+
defaultConfig: () => ({ metric: 'count' }),
|
|
28
|
+
Render: CategoricalRender,
|
|
29
|
+
EditForm: ({ draft, update, ctx }) => (_jsxs(_Fragment, { children: [_jsx(MetricField, { draft: draft, update: update, ctx: ctx }), _jsx(EventTypeField, { draft: draft, update: update, ctx: ctx })] })),
|
|
30
|
+
};
|
|
31
|
+
export const topTagsWidget = {
|
|
32
|
+
meta: { value: 'top_tags', label: 'Top tags', hint: 'Ranked tag-value list (needs prefix)' },
|
|
33
|
+
defaultConfig: () => ({ metric: 'count' }),
|
|
34
|
+
Render: CategoricalRender,
|
|
35
|
+
EditForm: ({ draft, update, ctx }) => (_jsxs(_Fragment, { children: [_jsx(TagPrefixField, { draft: draft, update: update, ctx: ctx }), _jsx(MetricField, { draft: draft, update: update, ctx: ctx })] })),
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=categorical.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"categorical.js","sourceRoot":"","sources":["../../src/widgets/categorical.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Render + form building blocks shared across widget kinds. */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { Theme, EditCtx, Widget } from './types.js';
|
|
4
|
+
export declare function NoData({ theme, label }: {
|
|
5
|
+
theme: Theme;
|
|
6
|
+
label?: string;
|
|
7
|
+
}): React.JSX.Element;
|
|
8
|
+
/** Horizontal ranked-bar list used by bar/pie/top_events/top_tags/funnel. */
|
|
9
|
+
export declare function RankedBars({ rows, theme, barHeight, }: {
|
|
10
|
+
rows: {
|
|
11
|
+
label: string;
|
|
12
|
+
count: number;
|
|
13
|
+
suffix?: string;
|
|
14
|
+
}[];
|
|
15
|
+
theme: Theme;
|
|
16
|
+
barHeight?: number;
|
|
17
|
+
}): React.JSX.Element;
|
|
18
|
+
/** Labeled field wrapper for EditForms. */
|
|
19
|
+
export declare function Field({ label, ctx, children }: {
|
|
20
|
+
label: string;
|
|
21
|
+
ctx: EditCtx;
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
}): React.JSX.Element;
|
|
24
|
+
/** Metric <select> reused by every value-based widget. */
|
|
25
|
+
export declare function MetricField({ draft, update, ctx, }: {
|
|
26
|
+
draft: Widget;
|
|
27
|
+
update: (patch: Partial<Widget>) => void;
|
|
28
|
+
ctx: EditCtx;
|
|
29
|
+
}): React.JSX.Element;
|
|
30
|
+
/** Optional event-type filter with autosuggest. */
|
|
31
|
+
export declare function EventTypeField({ draft, update, ctx, }: {
|
|
32
|
+
draft: Widget;
|
|
33
|
+
update: (patch: Partial<Widget>) => void;
|
|
34
|
+
ctx: EditCtx;
|
|
35
|
+
}): React.JSX.Element;
|
|
36
|
+
/** Tag-prefix field with autosuggest. */
|
|
37
|
+
export declare function TagPrefixField({ draft, update, ctx, }: {
|
|
38
|
+
draft: Widget;
|
|
39
|
+
update: (patch: Partial<Widget>) => void;
|
|
40
|
+
ctx: EditCtx;
|
|
41
|
+
}): React.JSX.Element;
|
|
42
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/widgets/common.tsx"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAU,MAAM,YAAY,CAAC;AAIjE,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,KAAkB,EAAE,EAAE;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,qBAErF;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,KAAK,EACL,SAAa,GACd,EAAE;IACD,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1D,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,qBAgCA;AAED,2CAA2C;AAC3C,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAOzG;AAED,0DAA0D;AAC1D,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,MAAM,EACN,GAAG,GACJ,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACzC,GAAG,EAAE,OAAO,CAAC;CACd,qBAeA;AAED,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,MAAM,EACN,GAAG,GACJ,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACzC,GAAG,EAAE,OAAO,CAAC;CACd,qBAgBA;AAED,yCAAyC;AACzC,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,MAAM,EACN,GAAG,GACJ,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACzC,GAAG,EAAE,OAAO,CAAC;CACd,qBAgBA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ACCENT, subTextColor, trackColor } from './types.js';
|
|
3
|
+
import { AutosuggestInput } from './inputs.js';
|
|
4
|
+
export function NoData({ theme, label = 'No data.' }) {
|
|
5
|
+
return _jsx("div", { style: { fontSize: 12, color: subTextColor(theme) }, children: label });
|
|
6
|
+
}
|
|
7
|
+
/** Horizontal ranked-bar list used by bar/pie/top_events/top_tags/funnel. */
|
|
8
|
+
export function RankedBars({ rows, theme, barHeight = 8, }) {
|
|
9
|
+
const subtext = subTextColor(theme);
|
|
10
|
+
if (rows.length === 0)
|
|
11
|
+
return _jsx(NoData, { theme: theme });
|
|
12
|
+
const max = Math.max(...rows.map((r) => r.count), 1);
|
|
13
|
+
return (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: rows.map((r, i) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("div", { style: {
|
|
14
|
+
fontSize: 11,
|
|
15
|
+
color: subtext,
|
|
16
|
+
width: 120,
|
|
17
|
+
overflow: 'hidden',
|
|
18
|
+
textOverflow: 'ellipsis',
|
|
19
|
+
whiteSpace: 'nowrap',
|
|
20
|
+
fontFamily: 'monospace',
|
|
21
|
+
}, children: r.label }), _jsx("div", { style: { flex: 1, height: barHeight, background: trackColor(theme), borderRadius: 4, overflow: 'hidden' }, children: _jsx("div", { style: { width: `${(r.count / max) * 100}%`, height: '100%', background: ACCENT, borderRadius: 4 } }) }), _jsxs("div", { style: { fontSize: 11, color: subtext, minWidth: 50, textAlign: 'right' }, children: [r.count.toLocaleString(), r.suffix ?? ''] })] }, i))) }));
|
|
22
|
+
}
|
|
23
|
+
/** Labeled field wrapper for EditForms. */
|
|
24
|
+
export function Field({ label, ctx, children }) {
|
|
25
|
+
return (_jsxs("label", { children: [_jsx("span", { style: ctx.labelStyle, children: label }), children] }));
|
|
26
|
+
}
|
|
27
|
+
/** Metric <select> reused by every value-based widget. */
|
|
28
|
+
export function MetricField({ draft, update, ctx, }) {
|
|
29
|
+
return (_jsx(Field, { label: "Metric", ctx: ctx, children: _jsxs("select", { style: ctx.inputStyle, value: draft.metric ?? 'count', onChange: (e) => update({ metric: e.target.value }), children: [_jsx("option", { value: "count", children: "Count" }), _jsx("option", { value: "unique", children: "Unique visitors" }), _jsx("option", { value: "sum", children: "Sum" }), _jsx("option", { value: "avg", children: "Average" })] }) }));
|
|
30
|
+
}
|
|
31
|
+
/** Optional event-type filter with autosuggest. */
|
|
32
|
+
export function EventTypeField({ draft, update, ctx, }) {
|
|
33
|
+
return (_jsx(Field, { label: "Event type filter (optional)", ctx: ctx, children: _jsx(AutosuggestInput, { endpoint: ctx.endpoint, authKey: ctx.authKey, uid: ctx.uid, suggestPath: "events", value: draft.eventType ?? '', onChange: (v) => update({ eventType: v || undefined }), placeholder: "e.g. page_view", theme: ctx.theme, inputStyle: ctx.inputStyle }) }));
|
|
34
|
+
}
|
|
35
|
+
/** Tag-prefix field with autosuggest. */
|
|
36
|
+
export function TagPrefixField({ draft, update, ctx, }) {
|
|
37
|
+
return (_jsx(Field, { label: "Tag prefix", ctx: ctx, children: _jsx(AutosuggestInput, { endpoint: ctx.endpoint, authKey: ctx.authKey, uid: ctx.uid, suggestPath: "prefixes", value: draft.tagPrefix ?? '', onChange: (v) => update({ tagPrefix: v || undefined }), placeholder: "e.g. variant:", theme: ctx.theme, inputStyle: ctx.inputStyle }) }));
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=common.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/widgets/common.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"funnel.d.ts","sourceRoot":"","sources":["../../src/widgets/funnel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAA0C,MAAM,YAAY,CAAC;AA4MnF,eAAO,MAAM,YAAY,EAAE,QAK1B,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { ACCENT, subTextColor, trackColor } from './types.js';
|
|
3
|
+
import { Field, NoData } from './common.js';
|
|
4
|
+
import { AutosuggestInput, TagsAutosuggestInput } from './inputs.js';
|
|
5
|
+
/** Compact waterfall: per-step visitors bar + overall conversion %. */
|
|
6
|
+
function FunnelRender({ widget, data, theme }) {
|
|
7
|
+
const d = data ?? {};
|
|
8
|
+
const steps = d.steps ?? [];
|
|
9
|
+
if (steps.length === 0) {
|
|
10
|
+
const hint = (widget.steps?.length ?? 0) < 2 ? 'Add at least 2 steps to this funnel.' : 'No funnel data.';
|
|
11
|
+
return _jsx(NoData, { theme: theme, label: hint });
|
|
12
|
+
}
|
|
13
|
+
const subtext = subTextColor(theme);
|
|
14
|
+
const max = Math.max(...steps.map((s) => s.visitors), 1);
|
|
15
|
+
return (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: steps.map((s, i) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsxs("div", { style: {
|
|
16
|
+
fontSize: 11,
|
|
17
|
+
color: subtext,
|
|
18
|
+
width: 120,
|
|
19
|
+
overflow: 'hidden',
|
|
20
|
+
textOverflow: 'ellipsis',
|
|
21
|
+
whiteSpace: 'nowrap',
|
|
22
|
+
fontFamily: 'monospace',
|
|
23
|
+
}, children: [i + 1, ". ", s.type] }), _jsx("div", { style: { flex: 1, height: 10, background: trackColor(theme), borderRadius: 4, overflow: 'hidden' }, children: _jsx("div", { style: { width: `${(s.visitors / max) * 100}%`, height: '100%', background: ACCENT, borderRadius: 4 } }) }), _jsxs("div", { style: { fontSize: 11, color: subtext, minWidth: 78, textAlign: 'right', fontVariantNumeric: 'tabular-nums' }, children: [s.visitors.toLocaleString(), " \u00B7 ", Math.round(s.conversionOverall * 100), "%"] })] }, i))) }));
|
|
24
|
+
}
|
|
25
|
+
function FunnelEditForm({ draft, update, ctx }) {
|
|
26
|
+
const steps = draft.steps ?? [];
|
|
27
|
+
const setSteps = (next) => update({ steps: next });
|
|
28
|
+
const updateStep = (i, patch) => setSteps(steps.map((s, idx) => (idx === i ? { ...s, ...patch } : s)));
|
|
29
|
+
const stepBoxStyle = {
|
|
30
|
+
border: `1px solid ${ctx.theme === 'dark' ? '#334155' : '#cbd5e1'}`,
|
|
31
|
+
borderRadius: 8,
|
|
32
|
+
padding: 8,
|
|
33
|
+
display: 'flex',
|
|
34
|
+
flexDirection: 'column',
|
|
35
|
+
gap: 6,
|
|
36
|
+
};
|
|
37
|
+
return (_jsxs(_Fragment, { children: [_jsx(Field, { label: "Window (minutes between steps)", ctx: ctx, children: _jsx("input", { type: "number", min: 1, style: ctx.inputStyle, value: draft.windowMinutes ?? 30, onChange: (e) => update({ windowMinutes: Number(e.target.value) || 30 }) }) }), _jsxs("div", { children: [_jsx("div", { style: { ...ctx.labelStyle, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: _jsxs("span", { children: ["Steps (", steps.length, ")"] }) }), _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: steps.map((step, i) => (_jsxs("div", { style: stepBoxStyle, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }, children: [_jsxs("span", { style: { fontSize: 11, fontWeight: 600, color: ctx.subLabelColor }, children: ["Step ", i + 1] }), _jsxs("div", { style: { display: 'flex', gap: 2 }, children: [_jsx("button", { type: "button", title: "Move up", disabled: i === 0, onClick: () => {
|
|
38
|
+
if (i === 0)
|
|
39
|
+
return;
|
|
40
|
+
const next = steps.slice();
|
|
41
|
+
[next[i - 1], next[i]] = [next[i], next[i - 1]];
|
|
42
|
+
setSteps(next);
|
|
43
|
+
}, style: miniBtn(ctx.theme, i === 0), children: "\u2191" }), _jsx("button", { type: "button", title: "Move down", disabled: i === steps.length - 1, onClick: () => {
|
|
44
|
+
if (i === steps.length - 1)
|
|
45
|
+
return;
|
|
46
|
+
const next = steps.slice();
|
|
47
|
+
[next[i + 1], next[i]] = [next[i], next[i + 1]];
|
|
48
|
+
setSteps(next);
|
|
49
|
+
}, style: miniBtn(ctx.theme, i === steps.length - 1), children: "\u2193" }), _jsx("button", { type: "button", title: "Remove step", onClick: () => setSteps(steps.filter((_, idx) => idx !== i)), style: { ...miniBtn(ctx.theme, false), background: '#ef4444', color: '#fff' }, children: "\u00D7" })] })] }), _jsx(AutosuggestInput, { endpoint: ctx.endpoint, authKey: ctx.authKey, uid: ctx.uid, suggestPath: "events", value: step.type, onChange: (v) => updateStep(i, { type: v }), placeholder: "event type, e.g. signup", theme: ctx.theme, inputStyle: ctx.inputStyle }), _jsx(TagsAutosuggestInput, { endpoint: ctx.endpoint, authKey: ctx.authKey, uid: ctx.uid, tags: step.tags ?? [], onChange: (t) => updateStep(i, { tags: t }), placeholder: "step tag filters (optional)", theme: ctx.theme, inputStyle: ctx.inputStyle, inputId: `funnel-step-${i}-tags` })] }, i))) }), _jsx("button", { type: "button", onClick: () => setSteps([...steps, { type: '', tags: [] }]), style: {
|
|
50
|
+
marginTop: 8,
|
|
51
|
+
width: '100%',
|
|
52
|
+
padding: '6px 10px',
|
|
53
|
+
fontSize: 12,
|
|
54
|
+
fontWeight: 600,
|
|
55
|
+
borderRadius: 6,
|
|
56
|
+
border: `1px dashed ${ACCENT}`,
|
|
57
|
+
background: 'transparent',
|
|
58
|
+
color: ACCENT,
|
|
59
|
+
cursor: 'pointer',
|
|
60
|
+
}, children: "+ Add step" })] }), _jsx(Field, { label: "Funnel tag filter (optional, applies to all steps)", ctx: ctx, children: _jsx(TagsAutosuggestInput, { endpoint: ctx.endpoint, authKey: ctx.authKey, uid: ctx.uid, tags: draft.filterTags ?? [], onChange: (t) => update({ filterTags: t }), placeholder: "type to search, Enter to add", theme: ctx.theme, inputStyle: ctx.inputStyle, inputId: "funnel-filter-tags" }) })] }));
|
|
61
|
+
}
|
|
62
|
+
function miniBtn(theme, disabled) {
|
|
63
|
+
return {
|
|
64
|
+
background: theme === 'dark' ? '#475569' : '#e2e8f0',
|
|
65
|
+
color: theme === 'dark' ? '#f1f5f9' : '#1e293b',
|
|
66
|
+
border: 'none',
|
|
67
|
+
borderRadius: 4,
|
|
68
|
+
width: 22,
|
|
69
|
+
height: 22,
|
|
70
|
+
fontSize: 13,
|
|
71
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
72
|
+
opacity: disabled ? 0.3 : 1,
|
|
73
|
+
padding: 0,
|
|
74
|
+
lineHeight: 1,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export const funnelWidget = {
|
|
78
|
+
meta: { value: 'funnel', label: 'Funnel', hint: 'Funnel waterfall (inline steps)' },
|
|
79
|
+
defaultConfig: () => ({ steps: [], windowMinutes: 30, filterTags: [] }),
|
|
80
|
+
Render: FunnelRender,
|
|
81
|
+
EditForm: FunnelEditForm,
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=funnel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"funnel.js","sourceRoot":"","sources":["../../src/widgets/funnel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/widgets/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/widgets/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autosuggest inputs shared by every widget EditForm.
|
|
3
|
+
*
|
|
4
|
+
* Zero deps; each fetches /e/<key>/<uid>/suggest/* with a 150ms debounce.
|
|
5
|
+
* - AutosuggestInput — single value (event type / tag prefix)
|
|
6
|
+
* - TagsAutosuggestInput — multi value chips (tag filters)
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import type { Theme } from './types.js';
|
|
10
|
+
export declare function AutosuggestInput({ endpoint, authKey, uid, suggestPath, value, onChange, placeholder, theme, inputStyle, }: {
|
|
11
|
+
endpoint: string;
|
|
12
|
+
authKey: string;
|
|
13
|
+
uid: string;
|
|
14
|
+
suggestPath: 'events' | 'prefixes';
|
|
15
|
+
value: string;
|
|
16
|
+
onChange: (v: string) => void;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
theme: Theme;
|
|
19
|
+
inputStyle: React.CSSProperties;
|
|
20
|
+
}): React.JSX.Element;
|
|
21
|
+
export declare function TagsAutosuggestInput({ endpoint, authKey, uid, tags, onChange, placeholder, theme, inputStyle, inputId, }: {
|
|
22
|
+
endpoint: string;
|
|
23
|
+
authKey: string;
|
|
24
|
+
uid: string;
|
|
25
|
+
tags: string[];
|
|
26
|
+
onChange: (tags: string[]) => void;
|
|
27
|
+
placeholder?: string;
|
|
28
|
+
theme: Theme;
|
|
29
|
+
inputStyle: React.CSSProperties;
|
|
30
|
+
inputId?: string;
|
|
31
|
+
}): React.JSX.Element;
|
|
32
|
+
//# sourceMappingURL=inputs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inputs.d.ts","sourceRoot":"","sources":["../../src/widgets/inputs.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAgBxC,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,OAAO,EACP,GAAG,EACH,WAAW,EACX,KAAK,EACL,QAAQ,EACR,WAAW,EACX,KAAK,EACL,UAAU,GACX,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,QAAQ,GAAG,UAAU,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,KAAK,CAAC,aAAa,CAAC;CACjC,qBAqKA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,OAAO,EACP,GAAG,EACH,IAAI,EACJ,QAAQ,EACR,WAAW,EACX,KAAK,EACL,UAAU,EACV,OAA8B,GAC/B,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,KAAK,CAAC,aAAa,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,qBAkPA"}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Autosuggest inputs shared by every widget EditForm.
|
|
4
|
+
*
|
|
5
|
+
* Zero deps; each fetches /e/<key>/<uid>/suggest/* with a 150ms debounce.
|
|
6
|
+
* - AutosuggestInput — single value (event type / tag prefix)
|
|
7
|
+
* - TagsAutosuggestInput — multi value chips (tag filters)
|
|
8
|
+
*/
|
|
9
|
+
import { useState, useEffect } from 'react';
|
|
10
|
+
function useDebounced(value, delayMs) {
|
|
11
|
+
const [debounced, setDebounced] = useState(value);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const t = setTimeout(() => setDebounced(value), delayMs);
|
|
14
|
+
return () => clearTimeout(t);
|
|
15
|
+
}, [value, delayMs]);
|
|
16
|
+
return debounced;
|
|
17
|
+
}
|
|
18
|
+
export function AutosuggestInput({ endpoint, authKey, uid, suggestPath, value, onChange, placeholder, theme, inputStyle, }) {
|
|
19
|
+
const [text, setText] = useState(value);
|
|
20
|
+
const [open, setOpen] = useState(false);
|
|
21
|
+
const [highlight, setHighlight] = useState(0);
|
|
22
|
+
const [items, setItems] = useState([]);
|
|
23
|
+
const [loading, setLoading] = useState(false);
|
|
24
|
+
const debounced = useDebounced(text.trim(), 150);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
setText(value);
|
|
27
|
+
}, [value]);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
let cancelled = false;
|
|
30
|
+
if (!open || debounced.length === 0) {
|
|
31
|
+
setItems([]);
|
|
32
|
+
setLoading(false);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
setLoading(true);
|
|
36
|
+
const url = `${endpoint.replace(/\/$/, '')}/e/${authKey}/${uid}/suggest/${suggestPath}?q=${encodeURIComponent(debounced)}&limit=10`;
|
|
37
|
+
fetch(url)
|
|
38
|
+
.then((r) => (r.ok ? r.json() : Promise.reject(new Error(String(r.status)))))
|
|
39
|
+
.then((body) => {
|
|
40
|
+
if (cancelled)
|
|
41
|
+
return;
|
|
42
|
+
setItems(body.items ?? []);
|
|
43
|
+
setHighlight(0);
|
|
44
|
+
})
|
|
45
|
+
.catch(() => {
|
|
46
|
+
if (cancelled)
|
|
47
|
+
return;
|
|
48
|
+
setItems([]);
|
|
49
|
+
})
|
|
50
|
+
.finally(() => {
|
|
51
|
+
if (!cancelled)
|
|
52
|
+
setLoading(false);
|
|
53
|
+
});
|
|
54
|
+
return () => {
|
|
55
|
+
cancelled = true;
|
|
56
|
+
};
|
|
57
|
+
}, [endpoint, authKey, uid, suggestPath, debounced, open]);
|
|
58
|
+
const showItemCreate = text.trim().length > 0 && !items.some((it) => it.value === text.trim());
|
|
59
|
+
function commit(v) {
|
|
60
|
+
setText(v);
|
|
61
|
+
onChange(v);
|
|
62
|
+
setOpen(false);
|
|
63
|
+
}
|
|
64
|
+
function onKeyDown(e) {
|
|
65
|
+
const totalOptions = items.length + (showItemCreate ? 1 : 0);
|
|
66
|
+
if (e.key === 'ArrowDown' && totalOptions > 0) {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
setOpen(true);
|
|
69
|
+
setHighlight((h) => Math.min(h + 1, totalOptions - 1));
|
|
70
|
+
}
|
|
71
|
+
else if (e.key === 'ArrowUp' && totalOptions > 0) {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
setHighlight((h) => Math.max(h - 1, 0));
|
|
74
|
+
}
|
|
75
|
+
else if (e.key === 'Enter') {
|
|
76
|
+
if (open && totalOptions > 0) {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
if (highlight < items.length)
|
|
79
|
+
commit(items[highlight].value);
|
|
80
|
+
else
|
|
81
|
+
commit(text.trim());
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (e.key === 'Escape') {
|
|
85
|
+
setOpen(false);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const dropdownBg = theme === 'dark' ? '#0f172a' : '#ffffff';
|
|
89
|
+
const dropdownBorder = theme === 'dark' ? '#334155' : '#cbd5e1';
|
|
90
|
+
const hoverBg = theme === 'dark' ? '#1e293b' : '#eef2ff';
|
|
91
|
+
const subText = theme === 'dark' ? '#94a3b8' : '#64748b';
|
|
92
|
+
return (_jsxs("div", { style: { position: 'relative' }, children: [_jsx("input", { style: inputStyle, value: text, onChange: (e) => {
|
|
93
|
+
setText(e.target.value);
|
|
94
|
+
onChange(e.target.value);
|
|
95
|
+
setOpen(true);
|
|
96
|
+
setHighlight(0);
|
|
97
|
+
}, onFocus: () => setOpen(true), onBlur: () => setTimeout(() => setOpen(false), 120), onKeyDown: onKeyDown, placeholder: placeholder, autoComplete: "off", spellCheck: false }), open && text.trim().length > 0 && (_jsxs("div", { style: {
|
|
98
|
+
position: 'absolute',
|
|
99
|
+
top: 'calc(100% + 2px)',
|
|
100
|
+
left: 0,
|
|
101
|
+
right: 0,
|
|
102
|
+
background: dropdownBg,
|
|
103
|
+
border: `1px solid ${dropdownBorder}`,
|
|
104
|
+
borderRadius: 6,
|
|
105
|
+
boxShadow: '0 8px 24px rgba(0,0,0,0.15)',
|
|
106
|
+
zIndex: 1100,
|
|
107
|
+
maxHeight: 240,
|
|
108
|
+
overflowY: 'auto',
|
|
109
|
+
fontSize: 12,
|
|
110
|
+
}, onMouseDown: (e) => e.preventDefault(), children: [loading && _jsx("div", { style: { padding: '8px 10px', color: subText }, children: "Searching\u2026" }), !loading && items.length === 0 && !showItemCreate && (_jsx("div", { style: { padding: '8px 10px', color: subText }, children: "No matches." })), items.map((it, i) => (_jsxs("div", { onMouseEnter: () => setHighlight(i), onMouseDown: (e) => {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
commit(it.value);
|
|
113
|
+
}, style: {
|
|
114
|
+
padding: '6px 10px',
|
|
115
|
+
cursor: 'pointer',
|
|
116
|
+
display: 'flex',
|
|
117
|
+
justifyContent: 'space-between',
|
|
118
|
+
gap: 8,
|
|
119
|
+
background: i === highlight ? hoverBg : 'transparent',
|
|
120
|
+
borderRadius: 4,
|
|
121
|
+
fontFamily: suggestPath === 'prefixes' ? 'monospace' : undefined,
|
|
122
|
+
}, children: [_jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: it.value }), _jsx("span", { style: { color: subText, flexShrink: 0, fontVariantNumeric: 'tabular-nums' }, children: it.count.toLocaleString() })] }, it.value))), showItemCreate && (_jsxs("div", { onMouseEnter: () => setHighlight(items.length), onMouseDown: (e) => {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
commit(text.trim());
|
|
125
|
+
}, style: {
|
|
126
|
+
padding: '6px 10px',
|
|
127
|
+
cursor: 'pointer',
|
|
128
|
+
borderTop: `1px dashed ${dropdownBorder}`,
|
|
129
|
+
marginTop: items.length > 0 ? 4 : 0,
|
|
130
|
+
background: highlight === items.length ? hoverBg : 'transparent',
|
|
131
|
+
borderRadius: 4,
|
|
132
|
+
color: '#4f46e5',
|
|
133
|
+
fontStyle: 'italic',
|
|
134
|
+
}, children: ["Use \"", text.trim(), "\"", suggestPath === 'prefixes' ? ' (new prefix)' : ' (new)'] }))] }))] }));
|
|
135
|
+
}
|
|
136
|
+
export function TagsAutosuggestInput({ endpoint, authKey, uid, tags, onChange, placeholder, theme, inputStyle, inputId = 'tag-input-internal', }) {
|
|
137
|
+
const [text, setText] = useState('');
|
|
138
|
+
const [open, setOpen] = useState(false);
|
|
139
|
+
const [highlight, setHighlight] = useState(0);
|
|
140
|
+
const [items, setItems] = useState([]);
|
|
141
|
+
const [loading, setLoading] = useState(false);
|
|
142
|
+
const debounced = useDebounced(text.trim(), 150);
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
let cancelled = false;
|
|
145
|
+
if (!open || debounced.length === 0) {
|
|
146
|
+
setItems([]);
|
|
147
|
+
setLoading(false);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
setLoading(true);
|
|
151
|
+
const url = `${endpoint.replace(/\/$/, '')}/e/${authKey}/${uid}/suggest/tags?q=${encodeURIComponent(debounced)}&limit=10`;
|
|
152
|
+
fetch(url)
|
|
153
|
+
.then((r) => (r.ok ? r.json() : Promise.reject(new Error(String(r.status)))))
|
|
154
|
+
.then((body) => {
|
|
155
|
+
if (cancelled)
|
|
156
|
+
return;
|
|
157
|
+
setItems(body.items ?? []);
|
|
158
|
+
setHighlight(0);
|
|
159
|
+
})
|
|
160
|
+
.catch(() => {
|
|
161
|
+
if (cancelled)
|
|
162
|
+
return;
|
|
163
|
+
setItems([]);
|
|
164
|
+
})
|
|
165
|
+
.finally(() => {
|
|
166
|
+
if (!cancelled)
|
|
167
|
+
setLoading(false);
|
|
168
|
+
});
|
|
169
|
+
return () => {
|
|
170
|
+
cancelled = true;
|
|
171
|
+
};
|
|
172
|
+
}, [endpoint, authKey, uid, debounced, open]);
|
|
173
|
+
function addTag(raw) {
|
|
174
|
+
const v = raw.trim();
|
|
175
|
+
if (!v)
|
|
176
|
+
return;
|
|
177
|
+
const next = Array.from(new Set([...tags, v]));
|
|
178
|
+
onChange(next);
|
|
179
|
+
setText('');
|
|
180
|
+
setItems([]);
|
|
181
|
+
setHighlight(0);
|
|
182
|
+
}
|
|
183
|
+
function removeTag(t) {
|
|
184
|
+
onChange(tags.filter((x) => x !== t));
|
|
185
|
+
}
|
|
186
|
+
function onKeyDown(e) {
|
|
187
|
+
const totalOptions = items.length + (text.trim().length > 0 && !items.some((it) => it.value === text.trim()) ? 1 : 0);
|
|
188
|
+
if (e.key === 'ArrowDown' && totalOptions > 0) {
|
|
189
|
+
e.preventDefault();
|
|
190
|
+
setOpen(true);
|
|
191
|
+
setHighlight((h) => Math.min(h + 1, totalOptions - 1));
|
|
192
|
+
}
|
|
193
|
+
else if (e.key === 'ArrowUp' && totalOptions > 0) {
|
|
194
|
+
e.preventDefault();
|
|
195
|
+
setHighlight((h) => Math.max(h - 1, 0));
|
|
196
|
+
}
|
|
197
|
+
else if (e.key === 'Enter' || e.key === ',') {
|
|
198
|
+
e.preventDefault();
|
|
199
|
+
if (open && totalOptions > 0) {
|
|
200
|
+
if (highlight < items.length)
|
|
201
|
+
addTag(items[highlight].value);
|
|
202
|
+
else
|
|
203
|
+
addTag(text.trim());
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
addTag(text.trim());
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else if (e.key === 'Backspace' && text === '' && tags.length > 0) {
|
|
210
|
+
removeTag(tags[tags.length - 1]);
|
|
211
|
+
}
|
|
212
|
+
else if (e.key === 'Escape') {
|
|
213
|
+
setOpen(false);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const dropdownBg = theme === 'dark' ? '#0f172a' : '#ffffff';
|
|
217
|
+
const dropdownBorder = theme === 'dark' ? '#334155' : '#cbd5e1';
|
|
218
|
+
const hoverBg = theme === 'dark' ? '#1e293b' : '#eef2ff';
|
|
219
|
+
const subText = theme === 'dark' ? '#94a3b8' : '#64748b';
|
|
220
|
+
const chipBg = theme === 'dark' ? '#334155' : '#e0e7ff';
|
|
221
|
+
const chipText = theme === 'dark' ? '#f1f5f9' : '#3730a3';
|
|
222
|
+
const showItemCreate = text.trim().length > 0 && !items.some((it) => it.value === text.trim());
|
|
223
|
+
return (_jsxs("div", { style: {
|
|
224
|
+
...inputStyle,
|
|
225
|
+
display: 'flex',
|
|
226
|
+
flexWrap: 'wrap',
|
|
227
|
+
gap: 4,
|
|
228
|
+
alignItems: 'center',
|
|
229
|
+
padding: '4px 6px',
|
|
230
|
+
cursor: 'text',
|
|
231
|
+
minHeight: inputStyle.padding ?? 36,
|
|
232
|
+
}, onMouseDown: () => {
|
|
233
|
+
const i = document.getElementById(inputId);
|
|
234
|
+
if (i)
|
|
235
|
+
i.focus();
|
|
236
|
+
}, children: [tags.map((t) => (_jsxs("span", { style: {
|
|
237
|
+
display: 'inline-flex',
|
|
238
|
+
alignItems: 'center',
|
|
239
|
+
gap: 4,
|
|
240
|
+
background: chipBg,
|
|
241
|
+
color: chipText,
|
|
242
|
+
padding: '2px 4px 2px 8px',
|
|
243
|
+
borderRadius: 4,
|
|
244
|
+
fontSize: 11,
|
|
245
|
+
fontFamily: 'monospace',
|
|
246
|
+
}, children: [t, _jsx("button", { type: "button", onMouseDown: (e) => {
|
|
247
|
+
e.preventDefault();
|
|
248
|
+
e.stopPropagation();
|
|
249
|
+
removeTag(t);
|
|
250
|
+
}, style: {
|
|
251
|
+
border: 'none',
|
|
252
|
+
background: 'transparent',
|
|
253
|
+
color: chipText,
|
|
254
|
+
cursor: 'pointer',
|
|
255
|
+
fontSize: 13,
|
|
256
|
+
lineHeight: 1,
|
|
257
|
+
padding: '0 4px',
|
|
258
|
+
}, "aria-label": `Remove ${t}`, children: "\u00D7" })] }, t))), _jsxs("div", { style: { position: 'relative', flex: 1, minWidth: 80 }, children: [_jsx("input", { id: inputId, style: {
|
|
259
|
+
width: '100%',
|
|
260
|
+
background: 'transparent',
|
|
261
|
+
border: 'none',
|
|
262
|
+
outline: 'none',
|
|
263
|
+
color: 'inherit',
|
|
264
|
+
fontSize: 13,
|
|
265
|
+
padding: '4px 2px',
|
|
266
|
+
fontFamily: 'monospace',
|
|
267
|
+
}, value: text, onChange: (e) => {
|
|
268
|
+
const next = e.target.value.replace(/,/g, '');
|
|
269
|
+
setText(next);
|
|
270
|
+
setOpen(true);
|
|
271
|
+
setHighlight(0);
|
|
272
|
+
}, onFocus: () => setOpen(true), onBlur: () => setTimeout(() => setOpen(false), 120), onKeyDown: onKeyDown, placeholder: tags.length === 0 ? placeholder : '', autoComplete: "off", spellCheck: false }), open && text.trim().length > 0 && (_jsxs("div", { style: {
|
|
273
|
+
position: 'absolute',
|
|
274
|
+
top: 'calc(100% + 2px)',
|
|
275
|
+
left: 0,
|
|
276
|
+
right: 0,
|
|
277
|
+
background: dropdownBg,
|
|
278
|
+
border: `1px solid ${dropdownBorder}`,
|
|
279
|
+
borderRadius: 6,
|
|
280
|
+
boxShadow: '0 8px 24px rgba(0,0,0,0.15)',
|
|
281
|
+
zIndex: 1100,
|
|
282
|
+
maxHeight: 240,
|
|
283
|
+
overflowY: 'auto',
|
|
284
|
+
fontSize: 12,
|
|
285
|
+
}, onMouseDown: (e) => e.preventDefault(), children: [loading && _jsx("div", { style: { padding: '8px 10px', color: subText }, children: "Searching\u2026" }), !loading && items.length === 0 && !showItemCreate && (_jsx("div", { style: { padding: '8px 10px', color: subText }, children: "No matches." })), items.map((it, i) => (_jsxs("div", { onMouseEnter: () => setHighlight(i), onMouseDown: (e) => {
|
|
286
|
+
e.preventDefault();
|
|
287
|
+
addTag(it.value);
|
|
288
|
+
}, style: {
|
|
289
|
+
padding: '6px 10px',
|
|
290
|
+
cursor: 'pointer',
|
|
291
|
+
display: 'flex',
|
|
292
|
+
justifyContent: 'space-between',
|
|
293
|
+
gap: 8,
|
|
294
|
+
background: i === highlight ? hoverBg : 'transparent',
|
|
295
|
+
borderRadius: 4,
|
|
296
|
+
fontFamily: 'monospace',
|
|
297
|
+
}, children: [_jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: it.value }), _jsx("span", { style: { color: subText, flexShrink: 0, fontVariantNumeric: 'tabular-nums' }, children: it.count.toLocaleString() })] }, it.value))), showItemCreate && (_jsxs("div", { onMouseEnter: () => setHighlight(items.length), onMouseDown: (e) => {
|
|
298
|
+
e.preventDefault();
|
|
299
|
+
addTag(text.trim());
|
|
300
|
+
}, style: {
|
|
301
|
+
padding: '6px 10px',
|
|
302
|
+
cursor: 'pointer',
|
|
303
|
+
borderTop: `1px dashed ${dropdownBorder}`,
|
|
304
|
+
marginTop: items.length > 0 ? 4 : 0,
|
|
305
|
+
background: highlight === items.length ? hoverBg : 'transparent',
|
|
306
|
+
borderRadius: 4,
|
|
307
|
+
color: '#4f46e5',
|
|
308
|
+
fontStyle: 'italic',
|
|
309
|
+
fontFamily: 'monospace',
|
|
310
|
+
}, children: ["Add \"", text.trim(), "\" (new tag)"] }))] }))] })] }));
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=inputs.js.map
|