@tcn/ui-time-selector 1.0.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/dist/components/date_range_panel/date_range_panel.d.ts +13 -0
- package/dist/components/date_range_panel/date_range_panel.d.ts.map +1 -0
- package/dist/components/date_range_panel/date_range_panel.js +71 -0
- package/dist/components/date_range_panel/date_range_panel.js.map +1 -0
- package/dist/components/date_range_panel/date_range_panel_presenter.d.ts +14 -0
- package/dist/components/date_range_panel/date_range_panel_presenter.d.ts.map +1 -0
- package/dist/components/date_range_panel/date_range_panel_presenter.js +24 -0
- package/dist/components/date_range_panel/date_range_panel_presenter.js.map +1 -0
- package/dist/components/preset_panel/preset_panel.d.ts +11 -0
- package/dist/components/preset_panel/preset_panel.d.ts.map +1 -0
- package/dist/components/preset_panel/preset_panel.js +52 -0
- package/dist/components/preset_panel/preset_panel.js.map +1 -0
- package/dist/components/preset_panel/preset_panel_presenter.d.ts +24 -0
- package/dist/components/preset_panel/preset_panel_presenter.d.ts.map +1 -0
- package/dist/components/preset_panel/preset_panel_presenter.js +39 -0
- package/dist/components/preset_panel/preset_panel_presenter.js.map +1 -0
- package/dist/components/time_selector/time_selector.d.ts +12 -0
- package/dist/components/time_selector/time_selector.d.ts.map +1 -0
- package/dist/components/time_selector/time_selector.js +105 -0
- package/dist/components/time_selector/time_selector.js.map +1 -0
- package/dist/components/time_selector/time_selector_presenter.d.ts +28 -0
- package/dist/components/time_selector/time_selector_presenter.d.ts.map +1 -0
- package/dist/components/time_selector/time_selector_presenter.js +66 -0
- package/dist/components/time_selector/time_selector_presenter.js.map +1 -0
- package/dist/components/timezone_footer/timezone_footer.d.ts +9 -0
- package/dist/components/timezone_footer/timezone_footer.d.ts.map +1 -0
- package/dist/components/timezone_footer/timezone_footer.js +78 -0
- package/dist/components/timezone_footer/timezone_footer.js.map +1 -0
- package/dist/components/timezone_footer/timezone_footer_presenter.d.ts +23 -0
- package/dist/components/timezone_footer/timezone_footer_presenter.d.ts.map +1 -0
- package/dist/components/timezone_footer/timezone_footer_presenter.js +56 -0
- package/dist/components/timezone_footer/timezone_footer_presenter.js.map +1 -0
- package/dist/components/utils.d.ts +32 -0
- package/dist/components/utils.d.ts.map +1 -0
- package/dist/components/utils.js +62 -0
- package/dist/components/utils.js.map +1 -0
- package/dist/date_range_panel.css +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/preset_panel.css +1 -0
- package/dist/time_selector.css +1 -0
- package/dist/timezone_footer.css +1 -0
- package/package.json +72 -0
- package/src/__stories__/time_selector.stories.tsx +99 -0
- package/src/components/date_range_panel/date_range_panel.module.css +31 -0
- package/src/components/date_range_panel/date_range_panel.tsx +87 -0
- package/src/components/date_range_panel/date_range_panel_presenter.ts +35 -0
- package/src/components/preset_panel/preset_panel.module.css +35 -0
- package/src/components/preset_panel/preset_panel.tsx +74 -0
- package/src/components/preset_panel/preset_panel_presenter.ts +68 -0
- package/src/components/time_selector/time_selector.module.css +27 -0
- package/src/components/time_selector/time_selector.tsx +100 -0
- package/src/components/time_selector/time_selector_presenter.ts +116 -0
- package/src/components/timezone_footer/timezone_footer.module.css +28 -0
- package/src/components/timezone_footer/timezone_footer.tsx +109 -0
- package/src/components/timezone_footer/timezone_footer_presenter.ts +83 -0
- package/src/components/utils.ts +95 -0
- package/src/index.ts +5 -0
- package/tsconfig.json +7 -0
- package/types/file_types.d.ts +54 -0
- package/types/react_color.d.ts +61 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsxs as l, jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import { useSignalValue as i } from "@tcn/state";
|
|
3
|
+
import { VStack as _, HStack as u, Spacer as b } from "@tcn/ui/stacks";
|
|
4
|
+
import { BodyText as c } from "@tcn/ui/typography";
|
|
5
|
+
import { Select as v, Option as S } from "@tcn/ui/inputs";
|
|
6
|
+
import { Button as w } from "@tcn/ui/actions";
|
|
7
|
+
import { useMemo as y } from "react";
|
|
8
|
+
import { ChevronDownIcon as T } from "@tcn/icons/chevron_down_icon.js";
|
|
9
|
+
import { ChevronUpIcon as C } from "@tcn/icons/chevron_up_icon.js";
|
|
10
|
+
import { getTimezoneOffsetMinutes as N, formatUTCOffset as x, getTimezoneAbbreviation as M } from "../utils.js";
|
|
11
|
+
import '../../timezone_footer.css';const k = "_utc-footer_5c1fa0d", A = "_utc-label_0f42c44", B = "_utc-offset_8a037cb", I = "_timezone-settings-button_cd0743e", L = "_timezone-select_0f93d05", V = "_timezone-option-offset_89f4289", s = { "utc-footer": k, "utc-label": A, "utc-offset": B, "timezone-settings-button": I, "timezone-select": L, "timezone-option-offset": V };
|
|
12
|
+
function K({
|
|
13
|
+
presenter: o,
|
|
14
|
+
timezones: m,
|
|
15
|
+
onTimezoneChange: d
|
|
16
|
+
}) {
|
|
17
|
+
const h = i(o.broadcasts.timezone), f = i(o.broadcasts.isSelectOpen), z = i(o.broadcasts.timezoneAbbreviation), p = i(o.broadcasts.timezoneLabel), g = i(o.broadcasts.utcOffset), O = y(() => {
|
|
18
|
+
const e = m ?? Intl.supportedValuesOf("timeZone"), r = /* @__PURE__ */ new Date();
|
|
19
|
+
return e.map((n) => {
|
|
20
|
+
const a = N(n, r);
|
|
21
|
+
return {
|
|
22
|
+
iana: n,
|
|
23
|
+
abbreviation: M(n, r),
|
|
24
|
+
utcOffset: x(a),
|
|
25
|
+
offsetMinutes: a
|
|
26
|
+
};
|
|
27
|
+
}).sort((n, a) => a.offsetMinutes - n.offsetMinutes);
|
|
28
|
+
}, [m]);
|
|
29
|
+
return /* @__PURE__ */ l(_, { className: s["utc-footer"], height: "auto", width: "100%", children: [
|
|
30
|
+
/* @__PURE__ */ l(u, { height: "auto", width: "100%", children: [
|
|
31
|
+
/* @__PURE__ */ t(c, { className: s["utc-label"], selectable: !1, size: "md", children: p }),
|
|
32
|
+
/* @__PURE__ */ t(b, {}),
|
|
33
|
+
/* @__PURE__ */ t(c, { selectable: !1, size: "md", className: s["utc-offset"], children: g }),
|
|
34
|
+
/* @__PURE__ */ l(
|
|
35
|
+
w,
|
|
36
|
+
{
|
|
37
|
+
size: "sm",
|
|
38
|
+
hierarchy: "secondary",
|
|
39
|
+
className: s["timezone-settings-button"],
|
|
40
|
+
onClick: () => o.toggleSelect(),
|
|
41
|
+
children: [
|
|
42
|
+
"Change time settings",
|
|
43
|
+
f ? /* @__PURE__ */ t(C, { size: "sm" }) : /* @__PURE__ */ t(T, { size: "sm" })
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
] }),
|
|
48
|
+
f && /* @__PURE__ */ t(
|
|
49
|
+
v,
|
|
50
|
+
{
|
|
51
|
+
value: h,
|
|
52
|
+
onChange: (e) => o.setTimezone(e, d),
|
|
53
|
+
hierarchy: "secondary",
|
|
54
|
+
className: s["timezone-select"],
|
|
55
|
+
size: "sm",
|
|
56
|
+
children: O.map((e) => /* @__PURE__ */ t(
|
|
57
|
+
S,
|
|
58
|
+
{
|
|
59
|
+
value: e.iana,
|
|
60
|
+
label: z,
|
|
61
|
+
keywords: [e.iana, e.abbreviation],
|
|
62
|
+
children: /* @__PURE__ */ l(u, { height: "auto", width: "100%", gap: "8px", children: [
|
|
63
|
+
/* @__PURE__ */ t(c, { selectable: !1, children: e.iana }),
|
|
64
|
+
/* @__PURE__ */ t(b, {}),
|
|
65
|
+
/* @__PURE__ */ t(c, { selectable: !1, children: e.abbreviation }),
|
|
66
|
+
/* @__PURE__ */ t(c, { className: s["timezone-option-offset"], selectable: !1, children: e.utcOffset })
|
|
67
|
+
] })
|
|
68
|
+
},
|
|
69
|
+
e.iana
|
|
70
|
+
))
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
] });
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
K as TimezoneFooter
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=timezone_footer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timezone_footer.js","sources":["../../../src/components/timezone_footer/timezone_footer.tsx"],"sourcesContent":["import { useSignalValue } from '@tcn/state';\nimport { HStack, Spacer, VStack } from '@tcn/ui/stacks';\nimport { BodyText } from '@tcn/ui/typography';\nimport { Option, Select } from '@tcn/ui/inputs';\nimport { Button } from '@tcn/ui/actions';\nimport { useMemo } from 'react';\nimport { ChevronDownIcon } from '@tcn/icons/chevron_down_icon.js';\nimport { ChevronUpIcon } from '@tcn/icons/chevron_up_icon.js';\nimport {\n formatUTCOffset,\n getTimezoneAbbreviation,\n getTimezoneOffsetMinutes,\n} from '../utils.js';\nimport { TimezoneFooterPresenter } from './timezone_footer_presenter.js';\nimport styles from './timezone_footer.module.css';\n\ninterface TimezoneOption {\n iana: string;\n abbreviation: string;\n utcOffset: string;\n offsetMinutes: number;\n}\n\nexport interface TimezoneFooterOwnProps {\n presenter: TimezoneFooterPresenter;\n timezones?: string[];\n onTimezoneChange?: (tz: string) => void;\n}\n\nexport type TimezoneFooterProps = TimezoneFooterOwnProps;\n\nexport function TimezoneFooter({\n presenter,\n timezones,\n onTimezoneChange,\n}: TimezoneFooterProps) {\n const selectedTimezone = useSignalValue(presenter.broadcasts.timezone);\n const isSelectOpen = useSignalValue(presenter.broadcasts.isSelectOpen);\n const timezoneAbbreviation = useSignalValue(presenter.broadcasts.timezoneAbbreviation);\n const timezoneLabel = useSignalValue(presenter.broadcasts.timezoneLabel);\n const utcOffset = useSignalValue(presenter.broadcasts.utcOffset);\n\n const timezoneOptions = useMemo<TimezoneOption[]>(() => {\n const ianas: string[] =\n timezones ?? ((Intl as any).supportedValuesOf('timeZone') as string[]);\n const now = new Date();\n return ianas\n .map(iana => {\n const offsetMinutes = getTimezoneOffsetMinutes(iana, now);\n return {\n iana,\n abbreviation: getTimezoneAbbreviation(iana, now),\n utcOffset: formatUTCOffset(offsetMinutes),\n offsetMinutes,\n };\n })\n .sort((a, b) => b.offsetMinutes - a.offsetMinutes);\n }, [timezones]);\n\n return (\n <VStack className={styles['utc-footer']} height=\"auto\" width=\"100%\">\n <HStack height=\"auto\" width=\"100%\">\n <BodyText className={styles['utc-label']} selectable={false} size=\"md\">\n {timezoneLabel}\n </BodyText>\n <Spacer />\n <BodyText selectable={false} size=\"md\" className={styles['utc-offset']}>\n {utcOffset}\n </BodyText>\n <Button\n size=\"sm\"\n hierarchy=\"secondary\"\n className={styles['timezone-settings-button']}\n onClick={() => presenter.toggleSelect()}\n >\n Change time settings\n {isSelectOpen ? <ChevronUpIcon size=\"sm\" /> : <ChevronDownIcon size=\"sm\" />}\n </Button>\n </HStack>\n {isSelectOpen && (\n <Select\n value={selectedTimezone}\n onChange={tz => presenter.setTimezone(tz, onTimezoneChange)}\n hierarchy=\"secondary\"\n className={styles['timezone-select']}\n size=\"sm\"\n >\n {timezoneOptions.map(tz => (\n <Option\n key={tz.iana}\n value={tz.iana}\n label={timezoneAbbreviation}\n keywords={[tz.iana, tz.abbreviation]}\n >\n <HStack height=\"auto\" width=\"100%\" gap=\"8px\">\n <BodyText selectable={false}>{tz.iana}</BodyText>\n <Spacer />\n <BodyText selectable={false}>{tz.abbreviation}</BodyText>\n <BodyText className={styles['timezone-option-offset']} selectable={false}>\n {tz.utcOffset}\n </BodyText>\n </HStack>\n </Option>\n ))}\n </Select>\n )}\n </VStack>\n );\n}\n"],"names":["TimezoneFooter","presenter","timezones","onTimezoneChange","selectedTimezone","useSignalValue","isSelectOpen","timezoneAbbreviation","timezoneLabel","utcOffset","timezoneOptions","useMemo","ianas","now","iana","offsetMinutes","getTimezoneOffsetMinutes","getTimezoneAbbreviation","formatUTCOffset","a","b","jsxs","VStack","styles","HStack","jsx","BodyText","Spacer","Button","ChevronUpIcon","ChevronDownIcon","Select","tz","Option"],"mappings":";;;;;;;;;;;AA+BO,SAASA,EAAe;AAAA,EAC7B,WAAAC;AAAA,EACA,WAAAC;AAAA,EACA,kBAAAC;AACF,GAAwB;AACtB,QAAMC,IAAmBC,EAAeJ,EAAU,WAAW,QAAQ,GAC/DK,IAAeD,EAAeJ,EAAU,WAAW,YAAY,GAC/DM,IAAuBF,EAAeJ,EAAU,WAAW,oBAAoB,GAC/EO,IAAgBH,EAAeJ,EAAU,WAAW,aAAa,GACjEQ,IAAYJ,EAAeJ,EAAU,WAAW,SAAS,GAEzDS,IAAkBC,EAA0B,MAAM;AACtD,UAAMC,IACJV,KAAe,KAAa,kBAAkB,UAAU,GACpDW,wBAAU,KAAA;AAChB,WAAOD,EACJ,IAAI,CAAAE,MAAQ;AACX,YAAMC,IAAgBC,EAAyBF,GAAMD,CAAG;AACxD,aAAO;AAAA,QACL,MAAAC;AAAA,QACA,cAAcG,EAAwBH,GAAMD,CAAG;AAAA,QAC/C,WAAWK,EAAgBH,CAAa;AAAA,QACxC,eAAAA;AAAA,MAAA;AAAA,IAEJ,CAAC,EACA,KAAK,CAACI,GAAGC,MAAMA,EAAE,gBAAgBD,EAAE,aAAa;AAAA,EACrD,GAAG,CAACjB,CAAS,CAAC;AAEd,SACE,gBAAAmB,EAACC,KAAO,WAAWC,EAAO,YAAY,GAAG,QAAO,QAAO,OAAM,QAC3D,UAAA;AAAA,IAAA,gBAAAF,EAACG,GAAA,EAAO,QAAO,QAAO,OAAM,QAC1B,UAAA;AAAA,MAAA,gBAAAC,EAACC,GAAA,EAAS,WAAWH,EAAO,WAAW,GAAG,YAAY,IAAO,MAAK,MAC/D,UAAAf,EAAA,CACH;AAAA,wBACCmB,GAAA,EAAO;AAAA,MACR,gBAAAF,EAACC,GAAA,EAAS,YAAY,IAAO,MAAK,MAAK,WAAWH,EAAO,YAAY,GAClE,UAAAd,EAAA,CACH;AAAA,MACA,gBAAAY;AAAA,QAACO;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,WAAWL,EAAO,0BAA0B;AAAA,UAC5C,SAAS,MAAMtB,EAAU,aAAA;AAAA,UAC1B,UAAA;AAAA,YAAA;AAAA,YAEEK,sBAAgBuB,GAAA,EAAc,MAAK,MAAK,IAAK,gBAAAJ,EAACK,GAAA,EAAgB,MAAK,KAAA,CAAK;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAC3E,GACF;AAAA,IACCxB,KACC,gBAAAmB;AAAA,MAACM;AAAA,MAAA;AAAA,QACC,OAAO3B;AAAA,QACP,UAAU,CAAA4B,MAAM/B,EAAU,YAAY+B,GAAI7B,CAAgB;AAAA,QAC1D,WAAU;AAAA,QACV,WAAWoB,EAAO,iBAAiB;AAAA,QACnC,MAAK;AAAA,QAEJ,UAAAb,EAAgB,IAAI,CAAAsB,MACnB,gBAAAP;AAAA,UAACQ;AAAA,UAAA;AAAA,YAEC,OAAOD,EAAG;AAAA,YACV,OAAOzB;AAAA,YACP,UAAU,CAACyB,EAAG,MAAMA,EAAG,YAAY;AAAA,YAEnC,4BAACR,GAAA,EAAO,QAAO,QAAO,OAAM,QAAO,KAAI,OACrC,UAAA;AAAA,cAAA,gBAAAC,EAACC,GAAA,EAAS,YAAY,IAAQ,UAAAM,EAAG,MAAK;AAAA,gCACrCL,GAAA,EAAO;AAAA,cACR,gBAAAF,EAACC,GAAA,EAAS,YAAY,IAAQ,YAAG,cAAa;AAAA,cAC9C,gBAAAD,EAACC,KAAS,WAAWH,EAAO,wBAAwB,GAAG,YAAY,IAChE,UAAAS,EAAG,UAAA,CACN;AAAA,YAAA,EAAA,CACF;AAAA,UAAA;AAAA,UAZKA,EAAG;AAAA,QAAA,CAcX;AAAA,MAAA;AAAA,IAAA;AAAA,EACH,GAEJ;AAEJ;"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Signal, derive } from '@tcn/state';
|
|
2
|
+
export declare class TimezoneFooterPresenter {
|
|
3
|
+
private readonly _startDate;
|
|
4
|
+
private readonly _endDate;
|
|
5
|
+
private _timezone;
|
|
6
|
+
private _isSelectOpen;
|
|
7
|
+
private _timezoneAbbreviation;
|
|
8
|
+
private _timezoneLabel;
|
|
9
|
+
private _utcOffset;
|
|
10
|
+
readonly broadcasts: {
|
|
11
|
+
timezone: Signal<string>['broadcast'];
|
|
12
|
+
isSelectOpen: Signal<boolean>['broadcast'];
|
|
13
|
+
timezoneAbbreviation: ReturnType<typeof derive<string, string>>['broadcast'];
|
|
14
|
+
timezoneLabel: ReturnType<typeof derive<string, string>>['broadcast'];
|
|
15
|
+
utcOffset: ReturnType<typeof derive<string, string>>['broadcast'];
|
|
16
|
+
};
|
|
17
|
+
constructor(_startDate: Signal<Date | null>, _endDate: Signal<Date | null>);
|
|
18
|
+
getTimezone(): string;
|
|
19
|
+
getAbbreviation(): string;
|
|
20
|
+
toggleSelect(): void;
|
|
21
|
+
setTimezone(iana: string, onTimezoneChange?: (tz: string) => void): void;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=timezone_footer_presenter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timezone_footer_presenter.d.ts","sourceRoot":"","sources":["../../../src/components/timezone_footer/timezone_footer_presenter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAS5C,qBAAa,uBAAuB;IAgBhC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAhB3B,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,qBAAqB,CAA4C;IACzE,OAAO,CAAC,cAAc,CAA4C;IAClE,OAAO,CAAC,UAAU,CAA4C;IAE9D,QAAQ,CAAC,UAAU,EAAE;QACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,CAAC;QACtC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC;QAC3C,oBAAoB,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QAC7E,aAAa,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACtE,SAAS,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;KACnE,CAAC;gBAGiB,UAAU,EAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,EAC/B,QAAQ,EAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;IAyBhD,WAAW,IAAI,MAAM;IAIrB,eAAe,IAAI,MAAM;IAIzB,YAAY;IAIZ,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI;CAmBlE"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Signal as a, derive as o } from "@tcn/state";
|
|
2
|
+
import { getTimezoneAbbreviation as m, formatUTCOffset as h, getTimezoneOffsetMinutes as l, unshiftFromDisplay as _, shiftForDisplay as c } from "../utils.js";
|
|
3
|
+
class d {
|
|
4
|
+
constructor(t, s) {
|
|
5
|
+
this._startDate = t, this._endDate = s;
|
|
6
|
+
const i = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
7
|
+
this._timezone = new a(i), this._timezoneAbbreviation = o(
|
|
8
|
+
this._timezone,
|
|
9
|
+
(e) => m(e, /* @__PURE__ */ new Date())
|
|
10
|
+
), this._timezoneLabel = o(
|
|
11
|
+
this._timezone,
|
|
12
|
+
(e) => `${e}, ${m(e, /* @__PURE__ */ new Date())}`
|
|
13
|
+
), this._utcOffset = o(
|
|
14
|
+
this._timezone,
|
|
15
|
+
(e) => h(l(e, /* @__PURE__ */ new Date()))
|
|
16
|
+
), this.broadcasts = {
|
|
17
|
+
timezone: this._timezone.broadcast,
|
|
18
|
+
isSelectOpen: this._isSelectOpen.broadcast,
|
|
19
|
+
timezoneAbbreviation: this._timezoneAbbreviation.broadcast,
|
|
20
|
+
timezoneLabel: this._timezoneLabel.broadcast,
|
|
21
|
+
utcOffset: this._utcOffset.broadcast
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
_timezone;
|
|
25
|
+
_isSelectOpen = new a(!1);
|
|
26
|
+
_timezoneAbbreviation;
|
|
27
|
+
_timezoneLabel;
|
|
28
|
+
_utcOffset;
|
|
29
|
+
broadcasts;
|
|
30
|
+
getTimezone() {
|
|
31
|
+
return this._timezone.get();
|
|
32
|
+
}
|
|
33
|
+
getAbbreviation() {
|
|
34
|
+
return this._timezoneAbbreviation.get();
|
|
35
|
+
}
|
|
36
|
+
toggleSelect() {
|
|
37
|
+
this._isSelectOpen.set(!this._isSelectOpen.get());
|
|
38
|
+
}
|
|
39
|
+
setTimezone(t, s) {
|
|
40
|
+
const i = this._timezone.get(), e = this._startDate.get();
|
|
41
|
+
if (e != null) {
|
|
42
|
+
const n = _(e, i);
|
|
43
|
+
this._startDate.set(c(n, t));
|
|
44
|
+
}
|
|
45
|
+
const r = this._endDate.get();
|
|
46
|
+
if (r != null) {
|
|
47
|
+
const n = _(r, i);
|
|
48
|
+
this._endDate.set(c(n, t));
|
|
49
|
+
}
|
|
50
|
+
this._timezone.set(t), this._isSelectOpen.set(!1), s?.(t);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
d as TimezoneFooterPresenter
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=timezone_footer_presenter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timezone_footer_presenter.js","sources":["../../../src/components/timezone_footer/timezone_footer_presenter.ts"],"sourcesContent":["import { Signal, derive } from '@tcn/state';\nimport {\n formatUTCOffset,\n getTimezoneAbbreviation,\n getTimezoneOffsetMinutes,\n shiftForDisplay,\n unshiftFromDisplay,\n} from '../utils.js';\n\nexport class TimezoneFooterPresenter {\n private _timezone: Signal<string>;\n private _isSelectOpen = new Signal<boolean>(false);\n private _timezoneAbbreviation: ReturnType<typeof derive<string, string>>;\n private _timezoneLabel: ReturnType<typeof derive<string, string>>;\n private _utcOffset: ReturnType<typeof derive<string, string>>;\n\n readonly broadcasts: {\n timezone: Signal<string>['broadcast'];\n isSelectOpen: Signal<boolean>['broadcast'];\n timezoneAbbreviation: ReturnType<typeof derive<string, string>>['broadcast'];\n timezoneLabel: ReturnType<typeof derive<string, string>>['broadcast'];\n utcOffset: ReturnType<typeof derive<string, string>>['broadcast'];\n };\n\n constructor(\n private readonly _startDate: Signal<Date | null>,\n private readonly _endDate: Signal<Date | null>\n ) {\n const browserTz = Intl.DateTimeFormat().resolvedOptions().timeZone;\n this._timezone = new Signal<string>(browserTz);\n\n this._timezoneAbbreviation = derive(this._timezone, tz =>\n getTimezoneAbbreviation(tz, new Date())\n );\n this._timezoneLabel = derive(\n this._timezone,\n tz => `${tz}, ${getTimezoneAbbreviation(tz, new Date())}`\n );\n this._utcOffset = derive(this._timezone, tz =>\n formatUTCOffset(getTimezoneOffsetMinutes(tz, new Date()))\n );\n\n this.broadcasts = {\n timezone: this._timezone.broadcast,\n isSelectOpen: this._isSelectOpen.broadcast,\n timezoneAbbreviation: this._timezoneAbbreviation.broadcast,\n timezoneLabel: this._timezoneLabel.broadcast,\n utcOffset: this._utcOffset.broadcast,\n };\n }\n\n getTimezone(): string {\n return this._timezone.get();\n }\n\n getAbbreviation(): string {\n return this._timezoneAbbreviation.get();\n }\n\n toggleSelect() {\n this._isSelectOpen.set(!this._isSelectOpen.get());\n }\n\n setTimezone(iana: string, onTimezoneChange?: (tz: string) => void) {\n const oldTz = this._timezone.get();\n\n const currentStart = this._startDate.get();\n if (currentStart != null) {\n const realStart = unshiftFromDisplay(currentStart, oldTz);\n this._startDate.set(shiftForDisplay(realStart, iana));\n }\n\n const currentEnd = this._endDate.get();\n if (currentEnd != null) {\n const realEnd = unshiftFromDisplay(currentEnd, oldTz);\n this._endDate.set(shiftForDisplay(realEnd, iana));\n }\n\n this._timezone.set(iana);\n this._isSelectOpen.set(false);\n onTimezoneChange?.(iana);\n }\n}\n"],"names":["TimezoneFooterPresenter","_startDate","_endDate","browserTz","Signal","derive","tz","getTimezoneAbbreviation","formatUTCOffset","getTimezoneOffsetMinutes","iana","onTimezoneChange","oldTz","currentStart","realStart","unshiftFromDisplay","shiftForDisplay","currentEnd","realEnd"],"mappings":";;AASO,MAAMA,EAAwB;AAAA,EAenC,YACmBC,GACAC,GACjB;AAFiB,SAAA,aAAAD,GACA,KAAA,WAAAC;AAEjB,UAAMC,IAAY,KAAK,eAAA,EAAiB,kBAAkB;AAC1D,SAAK,YAAY,IAAIC,EAAeD,CAAS,GAE7C,KAAK,wBAAwBE;AAAA,MAAO,KAAK;AAAA,MAAW,CAAAC,MAClDC,EAAwBD,GAAI,oBAAI,MAAM;AAAA,IAAA,GAExC,KAAK,iBAAiBD;AAAA,MACpB,KAAK;AAAA,MACL,CAAAC,MAAM,GAAGA,CAAE,KAAKC,EAAwBD,GAAI,oBAAI,KAAA,CAAM,CAAC;AAAA,IAAA,GAEzD,KAAK,aAAaD;AAAA,MAAO,KAAK;AAAA,MAAW,OACvCG,EAAgBC,EAAyBH,GAAI,oBAAI,KAAA,CAAM,CAAC;AAAA,IAAA,GAG1D,KAAK,aAAa;AAAA,MAChB,UAAU,KAAK,UAAU;AAAA,MACzB,cAAc,KAAK,cAAc;AAAA,MACjC,sBAAsB,KAAK,sBAAsB;AAAA,MACjD,eAAe,KAAK,eAAe;AAAA,MACnC,WAAW,KAAK,WAAW;AAAA,IAAA;AAAA,EAE/B;AAAA,EAvCQ;AAAA,EACA,gBAAgB,IAAIF,EAAgB,EAAK;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EAEC;AAAA,EAmCT,cAAsB;AACpB,WAAO,KAAK,UAAU,IAAA;AAAA,EACxB;AAAA,EAEA,kBAA0B;AACxB,WAAO,KAAK,sBAAsB,IAAA;AAAA,EACpC;AAAA,EAEA,eAAe;AACb,SAAK,cAAc,IAAI,CAAC,KAAK,cAAc,KAAK;AAAA,EAClD;AAAA,EAEA,YAAYM,GAAcC,GAAyC;AACjE,UAAMC,IAAQ,KAAK,UAAU,IAAA,GAEvBC,IAAe,KAAK,WAAW,IAAA;AACrC,QAAIA,KAAgB,MAAM;AACxB,YAAMC,IAAYC,EAAmBF,GAAcD,CAAK;AACxD,WAAK,WAAW,IAAII,EAAgBF,GAAWJ,CAAI,CAAC;AAAA,IACtD;AAEA,UAAMO,IAAa,KAAK,SAAS,IAAA;AACjC,QAAIA,KAAc,MAAM;AACtB,YAAMC,IAAUH,EAAmBE,GAAYL,CAAK;AACpD,WAAK,SAAS,IAAII,EAAgBE,GAASR,CAAI,CAAC;AAAA,IAClD;AAEA,SAAK,UAAU,IAAIA,CAAI,GACvB,KAAK,cAAc,IAAI,EAAK,GAC5BC,IAAmBD,CAAI;AAAA,EACzB;AACF;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the timezone offset in minutes using the same sign convention as
|
|
3
|
+
* Date.getTimezoneOffset(): positive = behind UTC (e.g. CDT/UTC-5 → +300),
|
|
4
|
+
* negative = ahead of UTC (e.g. CEST/UTC+2 → -120).
|
|
5
|
+
*/
|
|
6
|
+
export declare function getTimezoneOffsetMinutes(iana: string, date: Date): number;
|
|
7
|
+
/** Returns the short timezone abbreviation, e.g. "CDT", "CEST", "UTC". */
|
|
8
|
+
export declare function getTimezoneAbbreviation(iana: string, date: Date): string;
|
|
9
|
+
/**
|
|
10
|
+
* Formats an offset (in getTimezoneOffset sign convention) as "UTC+HH:MM" or "UTC-HH:MM".
|
|
11
|
+
* Positive offsetMinutes (behind UTC) → negative sign in display (e.g. +300 → "UTC-05:00").
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatUTCOffset(offsetMinutes: number): string;
|
|
14
|
+
/**
|
|
15
|
+
* Shifts a real UTC moment so that DatePickerInput (which always renders in the
|
|
16
|
+
* browser's local timezone) visually displays the wall-clock time of `targetIana`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function shiftForDisplay(date: Date, targetIana: string): Date;
|
|
19
|
+
/**
|
|
20
|
+
* Reverses shiftForDisplay: given a display date (shifted), recovers the real UTC moment.
|
|
21
|
+
*/
|
|
22
|
+
export declare function unshiftFromDisplay(displayDate: Date, targetIana: string): Date;
|
|
23
|
+
export declare function formatDate(date: Date): string;
|
|
24
|
+
export interface PresetItem {
|
|
25
|
+
label: string;
|
|
26
|
+
minutes: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function datesFromPreset(item: PresetItem): {
|
|
29
|
+
start: Date;
|
|
30
|
+
end: Date;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/components/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,CAuBzE;AAED,0EAA0E;AAC1E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,CAMxE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAKpE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAK9E;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAS7C;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,IAAI,CAAA;CAAE,CAI5E"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function i(t, n) {
|
|
2
|
+
const o = new Intl.DateTimeFormat("en-US", {
|
|
3
|
+
timeZone: t,
|
|
4
|
+
year: "numeric",
|
|
5
|
+
month: "2-digit",
|
|
6
|
+
day: "2-digit",
|
|
7
|
+
hour: "2-digit",
|
|
8
|
+
minute: "2-digit",
|
|
9
|
+
second: "2-digit",
|
|
10
|
+
hour12: !1
|
|
11
|
+
}).formatToParts(n), e = (a) => parseInt(o.find((m) => m.type === a)?.value ?? "0", 10), r = e("hour") % 24, s = Date.UTC(
|
|
12
|
+
e("year"),
|
|
13
|
+
e("month") - 1,
|
|
14
|
+
e("day"),
|
|
15
|
+
r,
|
|
16
|
+
e("minute"),
|
|
17
|
+
e("second")
|
|
18
|
+
);
|
|
19
|
+
return Math.round((n.getTime() - s) / 6e4);
|
|
20
|
+
}
|
|
21
|
+
function f(t, n) {
|
|
22
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
23
|
+
timeZone: t,
|
|
24
|
+
timeZoneName: "short"
|
|
25
|
+
}).formatToParts(n).find((e) => e.type === "timeZoneName")?.value ?? t;
|
|
26
|
+
}
|
|
27
|
+
function u(t) {
|
|
28
|
+
const n = t <= 0 ? "+" : "-", o = Math.abs(t), e = Math.floor(o / 60).toString().padStart(2, "0"), r = (o % 60).toString().padStart(2, "0");
|
|
29
|
+
return `UTC${n}${e}:${r}`;
|
|
30
|
+
}
|
|
31
|
+
function c(t, n) {
|
|
32
|
+
const o = t.getTimezoneOffset(), e = i(n, t), r = (o - e) * 6e4;
|
|
33
|
+
return new Date(t.getTime() + r);
|
|
34
|
+
}
|
|
35
|
+
function g(t, n) {
|
|
36
|
+
const o = t.getTimezoneOffset(), e = i(n, t), r = (o - e) * 6e4;
|
|
37
|
+
return new Date(t.getTime() - r);
|
|
38
|
+
}
|
|
39
|
+
function h(t) {
|
|
40
|
+
return t.toLocaleString("en-US", {
|
|
41
|
+
month: "short",
|
|
42
|
+
day: "numeric",
|
|
43
|
+
year: "numeric",
|
|
44
|
+
hour: "2-digit",
|
|
45
|
+
minute: "2-digit",
|
|
46
|
+
hour12: !1
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function T(t) {
|
|
50
|
+
const n = /* @__PURE__ */ new Date();
|
|
51
|
+
return { start: new Date(n.getTime() - t.minutes * 60 * 1e3), end: n };
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
T as datesFromPreset,
|
|
55
|
+
h as formatDate,
|
|
56
|
+
u as formatUTCOffset,
|
|
57
|
+
f as getTimezoneAbbreviation,
|
|
58
|
+
i as getTimezoneOffsetMinutes,
|
|
59
|
+
c as shiftForDisplay,
|
|
60
|
+
g as unshiftFromDisplay
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../src/components/utils.ts"],"sourcesContent":["/**\n * Returns the timezone offset in minutes using the same sign convention as\n * Date.getTimezoneOffset(): positive = behind UTC (e.g. CDT/UTC-5 → +300),\n * negative = ahead of UTC (e.g. CEST/UTC+2 → -120).\n */\nexport function getTimezoneOffsetMinutes(iana: string, date: Date): number {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: iana,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n }).formatToParts(date);\n const get = (type: string) =>\n parseInt(parts.find(p => p.type === type)?.value ?? '0', 10);\n const hour = get('hour') % 24; // handle 24 → 0\n const tzMs = Date.UTC(\n get('year'),\n get('month') - 1,\n get('day'),\n hour,\n get('minute'),\n get('second')\n );\n return Math.round((date.getTime() - tzMs) / 60000);\n}\n\n/** Returns the short timezone abbreviation, e.g. \"CDT\", \"CEST\", \"UTC\". */\nexport function getTimezoneAbbreviation(iana: string, date: Date): string {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: iana,\n timeZoneName: 'short',\n }).formatToParts(date);\n return parts.find(p => p.type === 'timeZoneName')?.value ?? iana;\n}\n\n/**\n * Formats an offset (in getTimezoneOffset sign convention) as \"UTC+HH:MM\" or \"UTC-HH:MM\".\n * Positive offsetMinutes (behind UTC) → negative sign in display (e.g. +300 → \"UTC-05:00\").\n */\nexport function formatUTCOffset(offsetMinutes: number): string {\n const sign = offsetMinutes <= 0 ? '+' : '-';\n const abs = Math.abs(offsetMinutes);\n const hours = Math.floor(abs / 60)\n .toString()\n .padStart(2, '0');\n const mins = (abs % 60).toString().padStart(2, '0');\n return `UTC${sign}${hours}:${mins}`;\n}\n\n/**\n * Shifts a real UTC moment so that DatePickerInput (which always renders in the\n * browser's local timezone) visually displays the wall-clock time of `targetIana`.\n */\nexport function shiftForDisplay(date: Date, targetIana: string): Date {\n const browserOffsetMin = date.getTimezoneOffset();\n const targetOffsetMin = getTimezoneOffsetMinutes(targetIana, date);\n const shiftMs = (browserOffsetMin - targetOffsetMin) * 60000;\n return new Date(date.getTime() + shiftMs);\n}\n\n/**\n * Reverses shiftForDisplay: given a display date (shifted), recovers the real UTC moment.\n */\nexport function unshiftFromDisplay(displayDate: Date, targetIana: string): Date {\n const browserOffsetMin = displayDate.getTimezoneOffset();\n const targetOffsetMin = getTimezoneOffsetMinutes(targetIana, displayDate);\n const shiftMs = (browserOffsetMin - targetOffsetMin) * 60000;\n return new Date(displayDate.getTime() - shiftMs);\n}\n\nexport function formatDate(date: Date): string {\n return date.toLocaleString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n });\n}\n\nexport interface PresetItem {\n label: string;\n minutes: number;\n}\n\nexport function datesFromPreset(item: PresetItem): { start: Date; end: Date } {\n const end = new Date();\n const start = new Date(end.getTime() - item.minutes * 60 * 1000);\n return { start, end };\n}\n"],"names":["getTimezoneOffsetMinutes","iana","date","parts","get","type","p","hour","tzMs","getTimezoneAbbreviation","formatUTCOffset","offsetMinutes","sign","abs","hours","mins","shiftForDisplay","targetIana","browserOffsetMin","targetOffsetMin","shiftMs","unshiftFromDisplay","displayDate","formatDate","datesFromPreset","item","end"],"mappings":"AAKO,SAASA,EAAyBC,GAAcC,GAAoB;AACzE,QAAMC,IAAQ,IAAI,KAAK,eAAe,SAAS;AAAA,IAC7C,UAAUF;AAAA,IACV,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EAAA,CACT,EAAE,cAAcC,CAAI,GACfE,IAAM,CAACC,MACX,SAASF,EAAM,KAAK,CAAAG,MAAKA,EAAE,SAASD,CAAI,GAAG,SAAS,KAAK,EAAE,GACvDE,IAAOH,EAAI,MAAM,IAAI,IACrBI,IAAO,KAAK;AAAA,IAChBJ,EAAI,MAAM;AAAA,IACVA,EAAI,OAAO,IAAI;AAAA,IACfA,EAAI,KAAK;AAAA,IACTG;AAAA,IACAH,EAAI,QAAQ;AAAA,IACZA,EAAI,QAAQ;AAAA,EAAA;AAEd,SAAO,KAAK,OAAOF,EAAK,QAAA,IAAYM,KAAQ,GAAK;AACnD;AAGO,SAASC,EAAwBR,GAAcC,GAAoB;AAKxE,SAJc,IAAI,KAAK,eAAe,SAAS;AAAA,IAC7C,UAAUD;AAAA,IACV,cAAc;AAAA,EAAA,CACf,EAAE,cAAcC,CAAI,EACR,KAAK,CAAAI,MAAKA,EAAE,SAAS,cAAc,GAAG,SAASL;AAC9D;AAMO,SAASS,EAAgBC,GAA+B;AAC7D,QAAMC,IAAOD,KAAiB,IAAI,MAAM,KAClCE,IAAM,KAAK,IAAIF,CAAa,GAC5BG,IAAQ,KAAK,MAAMD,IAAM,EAAE,EAC9B,WACA,SAAS,GAAG,GAAG,GACZE,KAAQF,IAAM,IAAI,WAAW,SAAS,GAAG,GAAG;AAClD,SAAO,MAAMD,CAAI,GAAGE,CAAK,IAAIC,CAAI;AACnC;AAMO,SAASC,EAAgBd,GAAYe,GAA0B;AACpE,QAAMC,IAAmBhB,EAAK,kBAAA,GACxBiB,IAAkBnB,EAAyBiB,GAAYf,CAAI,GAC3DkB,KAAWF,IAAmBC,KAAmB;AACvD,SAAO,IAAI,KAAKjB,EAAK,QAAA,IAAYkB,CAAO;AAC1C;AAKO,SAASC,EAAmBC,GAAmBL,GAA0B;AAC9E,QAAMC,IAAmBI,EAAY,kBAAA,GAC/BH,IAAkBnB,EAAyBiB,GAAYK,CAAW,GAClEF,KAAWF,IAAmBC,KAAmB;AACvD,SAAO,IAAI,KAAKG,EAAY,QAAA,IAAYF,CAAO;AACjD;AAEO,SAASG,EAAWrB,GAAoB;AAC7C,SAAOA,EAAK,eAAe,SAAS;AAAA,IAClC,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,EAAA,CACT;AACH;AAOO,SAASsB,EAAgBC,GAA8C;AAC5E,QAAMC,wBAAU,KAAA;AAEhB,SAAO,EAAE,OADK,IAAI,KAAKA,EAAI,YAAYD,EAAK,UAAU,KAAK,GAAI,GAC/C,KAAAC,EAAA;AAClB;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._date-picker-panel_1e6c441{border-right:1px solid var(--tcn-color-border, #d1d5db);padding:8px;gap:8px;flex:1;align-items:flex-start;justify-content:flex-start;align-self:stretch}._date-picker-panel-title_bc33101{font-weight:600;color:var(--tcn-color-text-primary, #111827);width:100%}._date-picker-section_90f5c3a{gap:4px;width:100%}._date-picker-label_113ebb5{font-weight:500;color:var(--tcn-color-text-secondary, #6b7280);display:flex;width:100%}._apply_05ed9af{margin-top:16px}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,6CAA6C,CAAC;AAC3E,YAAY,EACV,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,6CAA6C,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._presets-panel_7e3238f{flex:1;padding:8px;gap:0}._preset-search_113ae3a{margin-bottom:8px}._preset-list_191d980{overflow-x:hidden;overflow-y:auto;gap:2px}._preset-item_aa39cec{width:100%;padding:6px 8px;margin-left:6px;border-radius:4px;cursor:pointer}._preset-item_aa39cec:hover{background:var(--tcn-color-surface-hover, #f3f4f6)}._preset-item_aa39cec[data-selected=true]{background:var(--tcn-color-surface-selected, #eff6ff)}._preset-item_aa39cec[data-highlighted=true]{background:var(--tcn-color-surface-hover, #e5e7eb)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._trigger-abbreviation_8e22c20{color:var(--ergo-primary, #2563eb)}._trigger_1096f0b{display:inline-flex;align-items:center;border:1px solid var(--tcn-color-border, #d1d5db);border-radius:4px;padding:4px 8px;cursor:pointer;-webkit-user-select:none;user-select:none}._trigger_1096f0b:hover{border-color:var(--tcn-color-border-hover, #9ca3af)}._popper-content_9403264{background:var(--tcn-color-surface-primary, #ffffff);border:1px solid var(--tcn-color-border, #d1d5db);border-radius:6px;box-shadow:0 4px 16px #0000001f;overflow:hidden;align-items:flex-start;width:400px}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._utc-footer_5c1fa0d{border-top:1px solid var(--tcn-color-border, #d1d5db);padding:8px 12px;gap:8px}._utc-label_0f42c44{color:var(--tcn-color-text-secondary, #6b7280)}._utc-offset_8a037cb{padding-right:4px}._timezone-settings-button_cd0743e{flex-shrink:0}._timezone-select_0f93d05{width:100%}._timezone-option-offset_89f4289{color:var(--tcn-color-text-secondary, #6b7280);min-width:80px;text-align:right;padding-right:4px}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tcn/ui-time-selector",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "React time selector component",
|
|
6
|
+
"author": "TCN",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"access": "public",
|
|
12
|
+
"blackcatConfig": {
|
|
13
|
+
"storybook": {
|
|
14
|
+
"publish": true,
|
|
15
|
+
"title": "UI Time Selector",
|
|
16
|
+
"port": 6301
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"src",
|
|
22
|
+
"types",
|
|
23
|
+
"tsconfig.json"
|
|
24
|
+
],
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"@bc-monorepo/source": "./src/index.ts",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"sideEffects": [
|
|
37
|
+
"**/*.css"
|
|
38
|
+
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@tcn/icons": "2.4.0",
|
|
41
|
+
"@tcn/state": "1.3.4"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"react": "^18.2.0",
|
|
45
|
+
"react-dom": "^18.2.0",
|
|
46
|
+
"@tcn/ui": "^0.18.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/react-color": "^3.0.13",
|
|
50
|
+
"@tcn/ui": "0.18.1"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"start": "pnpm storybook",
|
|
54
|
+
"build": "vite build",
|
|
55
|
+
"clean": "rm -rf dist storybook-static",
|
|
56
|
+
"clean:all": "pnpm clean && rm -rf node_modules",
|
|
57
|
+
"check:all": "concurrently 'pnpm check:types' 'pnpm run biome check .'",
|
|
58
|
+
"check:types": "tsc --project tsconfig.typecheck.json --noEmit",
|
|
59
|
+
"check:format": "pnpm run biome format .",
|
|
60
|
+
"check:lint": "pnpm run biome lint .",
|
|
61
|
+
"check:imports": "pnpm run biome check --formatter-enabled=false --linter-enabled=false --assist-enabled=true",
|
|
62
|
+
"fix:all": "pnpm run biome check --write",
|
|
63
|
+
"fix:format": "pnpm run biome format --write",
|
|
64
|
+
"fix:lint": "pnpm run biome lint --write",
|
|
65
|
+
"fix:imports": "pnpm run biome check --write --unsafe --formatter-enabled=false --linter-enabled=false --assist-enabled=true",
|
|
66
|
+
"publish:dry-run": "pnpm build && pnpm publish --dry-run --force --no-git-checks",
|
|
67
|
+
"biome": "pnpm exec biome",
|
|
68
|
+
"storybook": "bash ../../scripts/ensure-blackcat-addon-built.sh && storybook dev",
|
|
69
|
+
"storybook:silent": "storybook dev --no-open --disable-telemetry --quiet",
|
|
70
|
+
"storybook:build": "storybook build"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Meta } from '@storybook/react-vite';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { TimeSelector } from '../components/time_selector/time_selector.js';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof TimeSelector> = {
|
|
6
|
+
title: 'TimeSelector',
|
|
7
|
+
component: TimeSelector,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
|
|
12
|
+
function twoWeeksAgo(): Date {
|
|
13
|
+
const d = new Date();
|
|
14
|
+
d.setDate(d.getDate() - 14);
|
|
15
|
+
return d;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function WithTwoWeekStartLimit() {
|
|
19
|
+
const [start, setStart] = useState<Date | null>(null);
|
|
20
|
+
const [end, setEnd] = useState<Date | null>(null);
|
|
21
|
+
const [timezone, setTimezone] = useState<string | null>(null);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
style={{ position: 'relative', minHeight: '100vh', padding: '16px', width: '100%' }}
|
|
26
|
+
>
|
|
27
|
+
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
|
28
|
+
<TimeSelector
|
|
29
|
+
startDateMin={twoWeeksAgo()}
|
|
30
|
+
onChange={(s, e) => {
|
|
31
|
+
setStart(s);
|
|
32
|
+
setEnd(e);
|
|
33
|
+
}}
|
|
34
|
+
onTimezoneChange={tz => setTimezone(tz)}
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
{start != null && end != null && (
|
|
38
|
+
<div
|
|
39
|
+
style={{
|
|
40
|
+
fontSize: '13px',
|
|
41
|
+
lineHeight: '1.6',
|
|
42
|
+
marginTop: '8px',
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
<div>
|
|
46
|
+
<strong>Start:</strong> {start.toISOString()}
|
|
47
|
+
</div>
|
|
48
|
+
<div>
|
|
49
|
+
<strong>End:</strong> {end.toISOString()}
|
|
50
|
+
</div>
|
|
51
|
+
<div>
|
|
52
|
+
<strong>Timezone:</strong> {timezone ?? '—'}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function Basic() {
|
|
61
|
+
const [start, setStart] = useState<Date | null>(null);
|
|
62
|
+
const [end, setEnd] = useState<Date | null>(null);
|
|
63
|
+
const [timezone, setTimezone] = useState<string | null>(null);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
style={{ position: 'relative', minHeight: '100vh', padding: '16px', width: '100%' }}
|
|
68
|
+
>
|
|
69
|
+
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
|
70
|
+
<TimeSelector
|
|
71
|
+
onChange={(s, e) => {
|
|
72
|
+
setStart(s);
|
|
73
|
+
setEnd(e);
|
|
74
|
+
}}
|
|
75
|
+
onTimezoneChange={tz => setTimezone(tz)}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
{start != null && end != null && (
|
|
79
|
+
<div
|
|
80
|
+
style={{
|
|
81
|
+
fontSize: '13px',
|
|
82
|
+
lineHeight: '1.6',
|
|
83
|
+
marginTop: '8px',
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<div>
|
|
87
|
+
<strong>Start:</strong> {start.toISOString()}
|
|
88
|
+
</div>
|
|
89
|
+
<div>
|
|
90
|
+
<strong>End:</strong> {end.toISOString()}
|
|
91
|
+
</div>
|
|
92
|
+
<div>
|
|
93
|
+
<strong>Timezone:</strong> {timezone ?? '—'}
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
)}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.date-picker-panel {
|
|
2
|
+
border-right: 1px solid var(--tcn-color-border, #d1d5db);
|
|
3
|
+
padding: 8px;
|
|
4
|
+
gap: 8px;
|
|
5
|
+
flex: 1;
|
|
6
|
+
align-items: flex-start;
|
|
7
|
+
justify-content: flex-start;
|
|
8
|
+
align-self: stretch;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.date-picker-panel-title {
|
|
12
|
+
font-weight: 600;
|
|
13
|
+
color: var(--tcn-color-text-primary, #111827);
|
|
14
|
+
width: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.date-picker-section {
|
|
18
|
+
gap: 4px;
|
|
19
|
+
width: 100%;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.date-picker-label {
|
|
23
|
+
font-weight: 500;
|
|
24
|
+
color: var(--tcn-color-text-secondary, #6b7280);
|
|
25
|
+
display: flex;
|
|
26
|
+
width: 100%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.apply {
|
|
30
|
+
margin-top: 16px;
|
|
31
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useSignalValue } from '@tcn/state';
|
|
2
|
+
import { Spacer, VStack } from '@tcn/ui/stacks';
|
|
3
|
+
import { BodyText } from '@tcn/ui/typography';
|
|
4
|
+
import { DatePickerInput } from '@tcn/ui/inputs';
|
|
5
|
+
import { Button } from '@tcn/ui/actions';
|
|
6
|
+
import { useEffect, useRef } from 'react';
|
|
7
|
+
import { IteratorIcon } from '@tcn/icons/iterator_icon.js';
|
|
8
|
+
import { DateRangePanelPresenter } from './date_range_panel_presenter.js';
|
|
9
|
+
import styles from './date_range_panel.module.css';
|
|
10
|
+
|
|
11
|
+
export interface DateRangePanelOwnProps {
|
|
12
|
+
presenter: DateRangePanelPresenter;
|
|
13
|
+
focused: boolean;
|
|
14
|
+
startDateMin?: Date | null;
|
|
15
|
+
startDateMax?: Date | null;
|
|
16
|
+
endDateMin?: Date | null;
|
|
17
|
+
endDateMax?: Date | null;
|
|
18
|
+
onApply: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type DateRangePanelProps = DateRangePanelOwnProps;
|
|
22
|
+
|
|
23
|
+
export function DateRangePanel({
|
|
24
|
+
presenter,
|
|
25
|
+
focused,
|
|
26
|
+
startDateMin,
|
|
27
|
+
startDateMax,
|
|
28
|
+
endDateMin,
|
|
29
|
+
endDateMax,
|
|
30
|
+
onApply,
|
|
31
|
+
}: DateRangePanelProps) {
|
|
32
|
+
const startDateRef = useRef<HTMLButtonElement>(null);
|
|
33
|
+
|
|
34
|
+
const startDate = useSignalValue(presenter.startDate.broadcast);
|
|
35
|
+
const endDate = useSignalValue(presenter.endDate.broadcast);
|
|
36
|
+
const isApplyDisabled = useSignalValue(presenter.broadcasts.isApplyDisabled);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (focused) {
|
|
40
|
+
startDateRef.current?.focus();
|
|
41
|
+
}
|
|
42
|
+
}, [focused]);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<VStack className={styles['date-picker-panel']} height="auto">
|
|
46
|
+
<BodyText className={styles['date-picker-panel-title']} selectable={false}>
|
|
47
|
+
Absolute time range
|
|
48
|
+
</BodyText>
|
|
49
|
+
<VStack className={styles['date-picker-section']} height="auto" width="auto">
|
|
50
|
+
<BodyText className={styles['date-picker-label']} selectable={false}>
|
|
51
|
+
From
|
|
52
|
+
</BodyText>
|
|
53
|
+
<DatePickerInput
|
|
54
|
+
ref={startDateRef}
|
|
55
|
+
showTime
|
|
56
|
+
value={startDate}
|
|
57
|
+
onChange={(date: Date | null) => presenter.setStartDate(date)}
|
|
58
|
+
min={startDateMin ?? undefined}
|
|
59
|
+
max={startDateMax ?? undefined}
|
|
60
|
+
/>
|
|
61
|
+
</VStack>
|
|
62
|
+
<VStack className={styles['date-picker-section']} height="auto" width="auto">
|
|
63
|
+
<BodyText className={styles['date-picker-label']} selectable={false}>
|
|
64
|
+
To
|
|
65
|
+
</BodyText>
|
|
66
|
+
<DatePickerInput
|
|
67
|
+
showTime
|
|
68
|
+
value={endDate}
|
|
69
|
+
onChange={(date: Date | null) => presenter.setEndDate(date)}
|
|
70
|
+
min={endDateMin ?? undefined}
|
|
71
|
+
max={endDateMax ?? undefined}
|
|
72
|
+
/>
|
|
73
|
+
</VStack>
|
|
74
|
+
<Button
|
|
75
|
+
size="sm"
|
|
76
|
+
hierarchy="primary"
|
|
77
|
+
disabled={isApplyDisabled}
|
|
78
|
+
onClick={onApply}
|
|
79
|
+
className={styles['apply']}
|
|
80
|
+
>
|
|
81
|
+
Apply time range
|
|
82
|
+
<Spacer width="8px" />
|
|
83
|
+
<IteratorIcon size="sm" />
|
|
84
|
+
</Button>
|
|
85
|
+
</VStack>
|
|
86
|
+
);
|
|
87
|
+
}
|