@gez/date-time-kit 2.0.0-alpha.1 → 2.0.0-alpha.2

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.
@@ -19,9 +19,15 @@ export interface Attrs extends BaseAttrs {
19
19
  * @default 'current-time'
20
20
  */
21
21
  'showing-time'?: string | number;
22
+ /**
23
+ * 选择器的粒度,表示最小可选的时间单位。默认为 millisecond。
24
+ * 例如设置为 'minute',则表示只能选择到分钟,秒和毫秒将被忽略。
25
+ */
26
+ 'min-granularity'?: 'day' | 'hour' | 'minute' | 'second' | 'millisecond';
22
27
  }
23
28
  export interface Emits {
24
29
  'select-time': Date;
30
+ 'open-change': boolean;
25
31
  }
26
32
  export type EventMap = Emit2EventMap<Emits>;
27
33
  export declare class Ele extends UiBase<Attrs, Emits> {
@@ -33,6 +39,8 @@ export declare class Ele extends UiBase<Attrs, Emits> {
33
39
  set showingTime(val: number | string | Date);
34
40
  get weekStartAt(): Weeks;
35
41
  set weekStartAt(val: Weeks);
42
+ get minGranularity(): "day" | "hour" | "minute" | "second" | "millisecond";
43
+ set minGranularity(val: 'day' | 'hour' | 'minute' | 'second' | 'millisecond');
36
44
  protected _style: string;
37
45
  protected _template: string;
38
46
  private get _navEle();
@@ -49,5 +57,5 @@ export declare class Ele extends UiBase<Attrs, Emits> {
49
57
  private _onTimePopoverOpenChange;
50
58
  private _onNavOpenToggle;
51
59
  private _onTimeSelectorDoneClick;
52
- timeFormatter: (time: Date) => string;
60
+ timeFormatter: (time: Date, minGranularity: "day" | "hour" | "minute" | "second" | "millisecond") => string;
53
61
  }
@@ -22,11 +22,18 @@ import { Ele as HhMmSsMsListGrpEle } from "../hhmmss-ms-list-grp/index.mjs";
22
22
  HhMmSsMsListGrpEle.define();
23
23
  import { Ele as PopoverEle } from "../popover/index.mjs";
24
24
  PopoverEle.define();
25
+ const granularityList = [
26
+ "day",
27
+ "hour",
28
+ "minute",
29
+ "second",
30
+ "millisecond"
31
+ ];
25
32
  export class Ele extends UiBase {
26
33
  constructor() {
27
34
  super();
28
35
  __publicField(this, "_style", styleStr);
29
- __publicField(this, "_template", html(_a || (_a = __template(['\n<div class="wrapper">\n <dt-yyyymm-nav\n show-ctrl-btn-month-add\n show-ctrl-btn-month-sub\n ></dt-yyyymm-nav>\n <dt-calendar-base></dt-calendar-base>\n <dt-popover>\n <div slot="trigger" class="time-echo-wrapper">\n <i class="time-icon"></i>\n <span class="time-echo">hh:mm:ss.sss</span>\n </div>\n <div slot="pop" class="time-selector">\n <h3 class="title">Select Time</h3>\n <dt-hhmmss-ms-list-grp></dt-hhmmss-ms-list-grp>\n <button id="time-selector-done-btn">Done</button>\n </div>\n </dt-popover>\n</div>\n']))));
36
+ __publicField(this, "_template", html(_a || (_a = __template(['\n<dt-popover>\n <slot slot="trigger" name="trigger"><button>select date and time</button></slot>\n <div slot="pop" class="wrapper menu">\n <dt-yyyymm-nav\n show-ctrl-btn-month-add\n show-ctrl-btn-month-sub\n ></dt-yyyymm-nav>\n <dt-calendar-base></dt-calendar-base>\n <dt-popover id="time-popover">\n <div slot="trigger" class="time-echo-wrapper">\n <i class="time-icon"></i>\n <span class="time-echo">hh:mm:ss.sss</span>\n </div>\n <div slot="pop" class="time-selector">\n <h3 class="title">Select Time</h3>\n <dt-hhmmss-ms-list-grp></dt-hhmmss-ms-list-grp>\n <button id="time-selector-done-btn">Done</button>\n </div>\n </dt-popover>\n </div>\n</dt-popover>\n']))));
30
37
  __publicField(this, "_render", debounce(() => {
31
38
  if (!this.isConnected) return;
32
39
  const currentTime = this.currentTime;
@@ -34,8 +41,16 @@ export class Ele extends UiBase {
34
41
  const tz = (/* @__PURE__ */ new Date()).getTimezoneOffset() * 60 * 1e3;
35
42
  this._navEle.millisecond = this._calendar.timeStart = this._calendar.timeEnd = +currentTime;
36
43
  this._calendar.showingTime = this.showingTime;
44
+ const selectorWrapper = this.shadowRoot.querySelector(".time-selector");
45
+ if (this.minGranularity === "day") {
46
+ this._timeSelector.millisecond = 0;
47
+ selectorWrapper.style.display = "none";
48
+ return;
49
+ }
50
+ selectorWrapper.style.display = "";
51
+ this._timeSelector.minGranularity = this.minGranularity;
37
52
  this._timeSelector.millisecond = (+currentTime - tz) % (24 * 60 * 60 * 1e3);
38
- this.shadowRoot.querySelector(".wrapper .time-echo").textContent = this.timeFormatter(currentTime);
53
+ this.shadowRoot.querySelector(".wrapper .time-echo").textContent = this.timeFormatter(currentTime, this.minGranularity);
39
54
  }, 0));
40
55
  __publicField(this, "_onCalendarSelect", (e) => {
41
56
  e.stopPropagation();
@@ -64,7 +79,6 @@ export class Ele extends UiBase {
64
79
  __publicField(this, "_onTimeSelectorDoneClick", (e) => {
65
80
  const btn = closestByEvent(e, "#time-selector-done-btn");
66
81
  if (!btn) return;
67
- const type = btn.dataset.type;
68
82
  const calcTime = (time, ms) => {
69
83
  time.setHours(0, 0, 0, 0);
70
84
  time.setMilliseconds(ms);
@@ -76,7 +90,14 @@ export class Ele extends UiBase {
76
90
  );
77
91
  this._timePopover.open = false;
78
92
  });
79
- __publicField(this, "timeFormatter", (time) => new Date(+time - (/* @__PURE__ */ new Date()).getTimezoneOffset() * 60 * 1e3).toISOString().slice(11, 23));
93
+ __publicField(this, "timeFormatter", (time, minGranularity) => {
94
+ const t = new Date(+time - (/* @__PURE__ */ new Date()).getTimezoneOffset() * 60 * 1e3).toISOString().slice(11, 23);
95
+ if (minGranularity === "day") return "";
96
+ if (minGranularity === "hour") return t.slice(0, 2);
97
+ if (minGranularity === "minute") return t.slice(0, 5);
98
+ if (minGranularity === "second") return t.slice(0, 8);
99
+ return t;
100
+ });
80
101
  this._applyTemplate();
81
102
  }
82
103
  static get observedAttributes() {
@@ -84,7 +105,8 @@ export class Ele extends UiBase {
84
105
  ...super.observedAttributes,
85
106
  "week-start-at",
86
107
  "current-time",
87
- "showing-time"
108
+ "showing-time",
109
+ "min-granularity"
88
110
  ];
89
111
  }
90
112
  get currentTime() {
@@ -112,6 +134,13 @@ export class Ele extends UiBase {
112
134
  if (!weekKey.includes(val)) return;
113
135
  this.setAttribute("week-start-at", val);
114
136
  }
137
+ get minGranularity() {
138
+ return this._getAttr("min-granularity", "millisecond");
139
+ }
140
+ set minGranularity(val) {
141
+ if (!granularityList.includes(val)) return;
142
+ this.setAttribute("min-granularity", val);
143
+ }
115
144
  get _navEle() {
116
145
  var _a2;
117
146
  return (_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelector("dt-yyyymm-nav");
@@ -130,7 +159,7 @@ export class Ele extends UiBase {
130
159
  }
131
160
  get _timePopover() {
132
161
  var _a2;
133
- return (_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelector("dt-popover");
162
+ return (_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelector("#time-popover");
134
163
  }
135
164
  connectedCallback() {
136
165
  var _a2;
@@ -174,6 +203,9 @@ export class Ele extends UiBase {
174
203
  _onAttrChanged(name, oldValue, newValue) {
175
204
  super._onAttrChanged(name, oldValue, newValue);
176
205
  this._render();
206
+ if (name === "current-time") {
207
+ this.dispatchEvent("select-time", this.currentTime);
208
+ }
177
209
  }
178
210
  }
179
211
  __publicField(Ele, "tagName", "dt-date-time-selector");
@@ -3,4 +3,4 @@ var __defProp = Object.defineProperty;
3
3
  var __template = (cooked, raw) => __freeze(__defProp(cooked, "raw", { value: __freeze(raw || cooked.slice()) }));
4
4
  var _a;
5
5
  import { css } from "../../utils.mjs";
6
- export const styleStr = css(_a || (_a = __template(["\n.wrapper {\n display: flex;\n flex-direction: column;\n gap: 15px;\n}\ndt-popover {\n position: relative;\n}\n\n[open] .time-echo-wrapper {\n border-color: #18181B;\n}\n\n.time-echo-wrapper {\n width: 100%;\n padding: 4px;\n display: flex;\n gap: 5px;\n border-radius: 4px;\n min-height: 30px;\n border: 1px solid #0001;\n box-sizing: border-box;\n align-items: center;\n cursor: pointer;\n}\n\n.time-selector {\n position: absolute;\n width: 100%;\n height: 461px;\n box-sizing: border-box;\n background-color: #fff;\n\n display: flex;\n flex-direction: column;\n gap: 15px;\n padding: 15px;\n border-radius: 6px;\n border: 1px solid #eee;\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n\n .title {\n font-size: 16px;\n margin: 0;\n line-height: 1;\n }\n}\n\ndt-hhmmss-ms-list-grp::part(list-container) {\n gap: 2px;\n}\ndt-hhmmss-ms-list-grp::part(list) {\n scroll-behavior: smooth;\n}\ndt-hhmmss-ms-list-grp::part(item) {\n font-size: 14px;\n line-height: 17px;\n}\n\n#time-selector-done-btn {\n border: none;\n min-height: 30px;\n border-radius: 6px;\n padding: 5px 10px;\n font-size: 14px;\n background-color: #18181B;\n color: #fff;\n}\n\n.time-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='15' fill='currentColor'%3E%3Cpath d='M7.4335 4.241a.4376.4376 0 0 0-.871.0594v3.783l.0044.0622a.4375.4375 0 0 0 .1921.3029L8.9242 9.877l.0566.0317a.4376.4376 0 0 0 .5495-.1559l.0317-.0566a.4376.4376 0 0 0-.1559-.5495L7.4375 7.8471V4.3004l-.004-.0593ZM7 1.6667c-3.2217 0-5.8333 2.6116-5.8333 5.8333 0 3.2217 2.6116 5.8333 5.8333 5.8333 3.2217 0 5.8333-2.6116 5.8333-5.8333 0-3.2217-2.6116-5.8333-5.8333-5.8333Zm0 .814c2.7721 0 5.0194 2.2472 5.0194 5.0193 0 2.7721-2.2473 5.0194-5.0194 5.0194S1.9806 10.2721 1.9806 7.5 4.228 2.4806 7 2.4806Z'/%3E%3C/svg%3E\") 50%/20px 20px no-repeat;\n}\n\n.time-echo {\n font-size: 14px;\n color: #999;\n line-height: 1;\n}\n\n"])));
6
+ export const styleStr = css(_a || (_a = __template(["\n.wrapper {\n display: flex;\n flex-direction: column;\n gap: 15px;\n}\ndt-popover {\n position: relative;\n}\n\n[open] > .time-echo-wrapper {\n border-color: #18181B;\n}\n\n.time-echo-wrapper {\n width: 100%;\n padding: 4px;\n display: flex;\n gap: 5px;\n border-radius: 4px;\n min-height: 30px;\n border: 1px solid #0001;\n box-sizing: border-box;\n align-items: center;\n cursor: pointer;\n}\n\n.time-selector {\n position: absolute;\n width: 100%;\n height: 461px;\n box-sizing: border-box;\n background-color: #fff;\n\n display: flex;\n flex-direction: column;\n gap: 15px;\n padding: 15px;\n border-radius: 6px;\n border: 1px solid #eee;\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n\n .title {\n font-size: 16px;\n margin: 0;\n line-height: 1;\n }\n}\n\ndt-hhmmss-ms-list-grp::part(list-container) {\n gap: 2px;\n}\ndt-hhmmss-ms-list-grp::part(list) {\n scroll-behavior: smooth;\n}\ndt-hhmmss-ms-list-grp::part(item) {\n font-size: 14px;\n line-height: 17px;\n}\n\n#time-selector-done-btn {\n border: none;\n min-height: 30px;\n border-radius: 6px;\n padding: 5px 10px;\n font-size: 14px;\n background-color: #18181B;\n color: #fff;\n}\n\n.time-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='15' fill='currentColor'%3E%3Cpath d='M7.4335 4.241a.4376.4376 0 0 0-.871.0594v3.783l.0044.0622a.4375.4375 0 0 0 .1921.3029L8.9242 9.877l.0566.0317a.4376.4376 0 0 0 .5495-.1559l.0317-.0566a.4376.4376 0 0 0-.1559-.5495L7.4375 7.8471V4.3004l-.004-.0593ZM7 1.6667c-3.2217 0-5.8333 2.6116-5.8333 5.8333 0 3.2217 2.6116 5.8333 5.8333 5.8333 3.2217 0 5.8333-2.6116 5.8333-5.8333 0-3.2217-2.6116-5.8333-5.8333-5.8333Zm0 .814c2.7721 0 5.0194 2.2472 5.0194 5.0193 0 2.7721-2.2473 5.0194-5.0194 5.0194S1.9806 10.2721 1.9806 7.5 4.228 2.4806 7 2.4806Z'/%3E%3C/svg%3E\") 50%/20px 20px no-repeat;\n}\n\n.time-echo {\n font-size: 14px;\n color: #999;\n line-height: 1;\n}\n\n.menu {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 10px 5px;\n font-size: 14px;\n gap: 10px;\n border-radius: 6px;\n border: 1px solid #eee;\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n background-color: #fff;\n width: 285px;\n\n & > * {\n width: 100%;\n box-sizing: border-box;\n }\n}\n\ndt-calendar-base {\n // 254 = item height 6 * 30 + week 14 + gap 10 * 6\n height: 254px;\n\n &::part(week) {\n font-size: 12px;\n line-height: 14px;\n }\n\n &::part(item) {\n font-size: 14px;\n }\n}\ndt-yyyymm-nav::part(list-grp) {\n height: 254px;\n margin-top: 10px;\n}\ndt-calendar-base.hide {\n display: none;\n}\n"])));
@@ -29,9 +29,9 @@ export declare class Ele extends UiBase<Attrs, Emits> {
29
29
  protected _style: string;
30
30
  protected _template: string;
31
31
  constructor();
32
- private get _colsHourEle();
33
- private get _colsMinuteEle();
34
- private get _colsSecondEle();
32
+ private get _listEleHour();
33
+ private get _listEleMinute();
34
+ private get _listEleSecond();
35
35
  private get _msInputEle();
36
36
  get millisecond(): number;
37
37
  set millisecond(v: number);
@@ -15,14 +15,14 @@ export class Ele extends UiBase {
15
15
  constructor() {
16
16
  super();
17
17
  __publicField(this, "_style", styleStr);
18
- __publicField(this, "_template", html(_b || (_b = __template(['\n <div class="cols" part="cols">\n <div class="col" part="col hour">\n <span>Hour</span>\n <dt-num-list\n exportparts="container:list-container, item, item-current"\n part="list hour"\n class="hour"\n min-num="0"\n max-num="23"\n ></dt-num-list>\n </div>\n <div class="col" part="col minute">\n <span>Minute</span>\n <dt-num-list\n exportparts="container:list-container, item, item-current"\n part="list minute"\n class="minute"\n min-num="0"\n max-num="59"\n ></dt-num-list>\n </div>\n <div class="col" part="col second">\n <span>Second</span>\n <dt-num-list\n exportparts="container:list-container, item, item-current"\n part="list second"\n class="second"\n min-num="0"\n max-num="59"\n ></dt-num-list>\n </div>\n </div>\n <label class="ms-input" part="ms-wrapper">\n <span part="ms-label">Millisecond</span>\n <input part="ms-input" id="ms" type="number" class="millisecond" min="0" max="999" step="1" placeholder="000" />\n </label>\n ']))));
18
+ __publicField(this, "_template", html(_b || (_b = __template(['\n <div class="cols" part="cols">\n <div class="col hour" part="col hour">\n <span>Hour</span>\n <dt-num-list\n exportparts="container:list-container, item, item-current"\n part="list hour"\n class="hour"\n min-num="0"\n max-num="23"\n ></dt-num-list>\n </div>\n <div class="col minute" part="col minute">\n <span>Minute</span>\n <dt-num-list\n exportparts="container:list-container, item, item-current"\n part="list minute"\n class="minute"\n min-num="0"\n max-num="59"\n ></dt-num-list>\n </div>\n <div class="col second" part="col second">\n <span>Second</span>\n <dt-num-list\n exportparts="container:list-container, item, item-current"\n part="list second"\n class="second"\n min-num="0"\n max-num="59"\n ></dt-num-list>\n </div>\n </div>\n <label class="ms-input" part="ms-wrapper">\n <span part="ms-label">Millisecond</span>\n <input part="ms-input" id="ms" type="number" class="millisecond" min="0" max="999" step="1" placeholder="000" />\n </label>\n ']))));
19
19
  __publicField(this, "_renderCols", debounce(() => {
20
20
  if (!this.isConnected) return;
21
21
  const {
22
22
  colOrder,
23
- _colsHourEle: hEle,
24
- _colsMinuteEle: mEle,
25
- _colsSecondEle: sEle
23
+ _listEleHour: hEle,
24
+ _listEleMinute: mEle,
25
+ _listEleSecond: sEle
26
26
  } = this;
27
27
  const orderedCols = [];
28
28
  for (const c of colOrder) {
@@ -39,13 +39,13 @@ export class Ele extends UiBase {
39
39
  __publicField(this, "_updateGranularity", debounce(() => {
40
40
  var _a2, _b2;
41
41
  if (!this.isConnected) return;
42
- const {
43
- _colsHourEle: hEle,
44
- _colsMinuteEle: mEle,
45
- _colsSecondEle: sEle,
46
- maxGranularity,
47
- minGranularity
48
- } = this;
42
+ const { maxGranularity, minGranularity } = this;
43
+ const hEle = this.shadowRoot.querySelector(".col.hour");
44
+ const mEle = this.shadowRoot.querySelector(".col.minute");
45
+ const sEle = this.shadowRoot.querySelector(".col.second");
46
+ const msEle = this.shadowRoot.querySelector(
47
+ '[part="ms-wrapper"]'
48
+ );
49
49
  const colsContainer = this.shadowRoot.querySelector(".cols");
50
50
  const granularityMap = {
51
51
  hour: 3,
@@ -62,16 +62,14 @@ export class Ele extends UiBase {
62
62
  colsContainer.style.display = [hEle, mEle, sEle].filter(
63
63
  (ele) => ele.style.display !== "none"
64
64
  ).length ? "" : "none";
65
- this.shadowRoot.querySelector(
66
- '[part="ms-wrapper"]'
67
- ).style.display = maxG >= 0 && minG <= 0 ? "" : "none";
65
+ msEle.style.display = maxG >= 0 && minG <= 0 ? "" : "none";
68
66
  }, 0));
69
67
  __publicField(this, "_updateColsValue", debounce(() => {
70
68
  if (!this.isConnected) return;
71
69
  const {
72
- _colsHourEle: hEle,
73
- _colsMinuteEle: mEle,
74
- _colsSecondEle: sEle,
70
+ _listEleHour: hEle,
71
+ _listEleMinute: mEle,
72
+ _listEleSecond: sEle,
75
73
  millisecond
76
74
  } = this;
77
75
  const hour = Math.floor(millisecond / (60 * 60 * 1e3));
@@ -132,17 +130,21 @@ export class Ele extends UiBase {
132
130
  "col-order"
133
131
  ];
134
132
  }
135
- get _colsHourEle() {
133
+ get _listEleHour() {
136
134
  var _a2;
137
- return (_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelector(".cols .hour");
135
+ return (_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelector("dt-num-list.hour");
138
136
  }
139
- get _colsMinuteEle() {
137
+ get _listEleMinute() {
140
138
  var _a2;
141
- return (_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelector(".cols .minute");
139
+ return (_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelector(
140
+ "dt-num-list.minute"
141
+ );
142
142
  }
143
- get _colsSecondEle() {
143
+ get _listEleSecond() {
144
144
  var _a2;
145
- return (_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelector(".cols .second");
145
+ return (_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelector(
146
+ "dt-num-list.second"
147
+ );
146
148
  }
147
149
  get _msInputEle() {
148
150
  var _a2;
@@ -185,23 +187,23 @@ export class Ele extends UiBase {
185
187
  }
186
188
  connectedCallback() {
187
189
  if (!super.connectedCallback()) return;
188
- this._colsHourEle.formatter = this._colsMinuteEle.formatter = this._colsSecondEle.formatter = (num) => ("0" + num).slice(-2);
190
+ this._listEleHour.formatter = this._listEleMinute.formatter = this._listEleSecond.formatter = (num) => ("0" + num).slice(-2);
189
191
  this._renderCols();
190
192
  this._updateGranularity();
191
193
  this._updateColsValue();
192
- this._colsHourEle.addEventListener("select-num", this._onColsSelect);
193
- this._colsMinuteEle.addEventListener("select-num", this._onColsSelect);
194
- this._colsSecondEle.addEventListener("select-num", this._onColsSelect);
194
+ this._listEleHour.addEventListener("select-num", this._onColsSelect);
195
+ this._listEleMinute.addEventListener("select-num", this._onColsSelect);
196
+ this._listEleSecond.addEventListener("select-num", this._onColsSelect);
195
197
  this._msInputEle.addEventListener("input", this._onMsInput);
196
198
  }
197
199
  disconnectedCallback() {
198
200
  if (!super.disconnectedCallback()) return;
199
- this._colsHourEle.removeEventListener("select-num", this._onColsSelect);
200
- this._colsMinuteEle.removeEventListener(
201
+ this._listEleHour.removeEventListener("select-num", this._onColsSelect);
202
+ this._listEleMinute.removeEventListener(
201
203
  "select-num",
202
204
  this._onColsSelect
203
205
  );
204
- this._colsSecondEle.removeEventListener(
206
+ this._listEleSecond.removeEventListener(
205
207
  "select-num",
206
208
  this._onColsSelect
207
209
  );
@@ -215,9 +217,9 @@ export class Ele extends UiBase {
215
217
  else if (name === "millisecond") this._updateColsValue();
216
218
  }
217
219
  _getMsFromEle() {
218
- const hour = this._colsHourEle.currentNum;
219
- const minute = this._colsMinuteEle.currentNum;
220
- const second = this._colsSecondEle.currentNum;
220
+ const hour = this._listEleHour.currentNum;
221
+ const minute = this._listEleMinute.currentNum;
222
+ const second = this._listEleSecond.currentNum;
221
223
  const ms = Math.min(Math.max(0, +this._msInputEle.value || 0), 999);
222
224
  return ((hour * 60 + minute) * 60 + second) * 1e3 + ms;
223
225
  }
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "unbuild": "3.6.0",
18
18
  "vitest": "3.2.4"
19
19
  },
20
- "version": "2.0.0-alpha.1",
20
+ "version": "2.0.0-alpha.2",
21
21
  "type": "module",
22
22
  "private": false,
23
23
  "exports": {
@@ -36,5 +36,5 @@
36
36
  "template",
37
37
  "public"
38
38
  ],
39
- "gitHead": "b093b967b089c135d5ad669c9412f93ed5d6d669"
39
+ "gitHead": "6dd8a158176b175cf723d37e851e9b41fdfcbefb"
40
40
  }
@@ -22,6 +22,14 @@ HhMmSsMsListGrpEle.define();
22
22
  import { Ele as PopoverEle, type EventMap as PopoverEvent } from '../popover';
23
23
  PopoverEle.define();
24
24
 
25
+ const granularityList = [
26
+ 'day',
27
+ 'hour',
28
+ 'minute',
29
+ 'second',
30
+ 'millisecond'
31
+ ] as const;
32
+
25
33
  export interface Attrs extends BaseAttrs {
26
34
  /**
27
35
  * Set which day of the week is the first day.
@@ -41,10 +49,16 @@ export interface Attrs extends BaseAttrs {
41
49
  * @default 'current-time'
42
50
  */
43
51
  'showing-time'?: string | number;
52
+ /**
53
+ * 选择器的粒度,表示最小可选的时间单位。默认为 millisecond。
54
+ * 例如设置为 'minute',则表示只能选择到分钟,秒和毫秒将被忽略。
55
+ */
56
+ 'min-granularity'?: 'day' | 'hour' | 'minute' | 'second' | 'millisecond';
44
57
  }
45
58
 
46
59
  export interface Emits {
47
60
  'select-time': Date;
61
+ 'open-change': boolean;
48
62
  }
49
63
  export type EventMap = Emit2EventMap<Emits>;
50
64
 
@@ -55,7 +69,8 @@ export class Ele extends UiBase<Attrs, Emits> {
55
69
  ...(super.observedAttributes as (keyof BaseAttrs)[]),
56
70
  'week-start-at',
57
71
  'current-time',
58
- 'showing-time'
72
+ 'showing-time',
73
+ 'min-granularity'
59
74
  ] satisfies (keyof Attrs)[];
60
75
  }
61
76
  public get currentTime() {
@@ -83,27 +98,42 @@ export class Ele extends UiBase<Attrs, Emits> {
83
98
  if (!weekKey.includes(val)) return;
84
99
  this.setAttribute('week-start-at', val);
85
100
  }
101
+ public get minGranularity() {
102
+ return this._getAttr('min-granularity', 'millisecond');
103
+ }
104
+ public set minGranularity(val:
105
+ | 'day'
106
+ | 'hour'
107
+ | 'minute'
108
+ | 'second'
109
+ | 'millisecond') {
110
+ if (!granularityList.includes(val)) return;
111
+ this.setAttribute('min-granularity', val);
112
+ }
86
113
 
87
114
  protected _style = styleStr;
88
115
  protected _template = html`
89
- <div class="wrapper">
90
- <dt-yyyymm-nav
91
- show-ctrl-btn-month-add
92
- show-ctrl-btn-month-sub
93
- ></dt-yyyymm-nav>
94
- <dt-calendar-base></dt-calendar-base>
95
- <dt-popover>
96
- <div slot="trigger" class="time-echo-wrapper">
97
- <i class="time-icon"></i>
98
- <span class="time-echo">hh:mm:ss.sss</span>
99
- </div>
100
- <div slot="pop" class="time-selector">
101
- <h3 class="title">Select Time</h3>
102
- <dt-hhmmss-ms-list-grp></dt-hhmmss-ms-list-grp>
103
- <button id="time-selector-done-btn">Done</button>
104
- </div>
105
- </dt-popover>
106
- </div>
116
+ <dt-popover>
117
+ <slot slot="trigger" name="trigger"><button>select date and time</button></slot>
118
+ <div slot="pop" class="wrapper menu">
119
+ <dt-yyyymm-nav
120
+ show-ctrl-btn-month-add
121
+ show-ctrl-btn-month-sub
122
+ ></dt-yyyymm-nav>
123
+ <dt-calendar-base></dt-calendar-base>
124
+ <dt-popover id="time-popover">
125
+ <div slot="trigger" class="time-echo-wrapper">
126
+ <i class="time-icon"></i>
127
+ <span class="time-echo">hh:mm:ss.sss</span>
128
+ </div>
129
+ <div slot="pop" class="time-selector">
130
+ <h3 class="title">Select Time</h3>
131
+ <dt-hhmmss-ms-list-grp></dt-hhmmss-ms-list-grp>
132
+ <button id="time-selector-done-btn">Done</button>
133
+ </div>
134
+ </dt-popover>
135
+ </div>
136
+ </dt-popover>
107
137
  `;
108
138
 
109
139
  private get _navEle() {
@@ -120,7 +150,7 @@ export class Ele extends UiBase<Attrs, Emits> {
120
150
  ) as HhMmSsMsListGrpEle;
121
151
  }
122
152
  private get _timePopover() {
123
- return this.shadowRoot?.querySelector('dt-popover') as PopoverEle;
153
+ return this.shadowRoot?.querySelector('#time-popover') as PopoverEle;
124
154
  }
125
155
 
126
156
  constructor() {
@@ -172,6 +202,9 @@ export class Ele extends UiBase<Attrs, Emits> {
172
202
  protected _onAttrChanged(name: string, oldValue: string, newValue: string) {
173
203
  super._onAttrChanged(name, oldValue, newValue);
174
204
  this._render();
205
+ if (name === 'current-time') {
206
+ this.dispatchEvent('select-time', this.currentTime as Date);
207
+ }
175
208
  }
176
209
 
177
210
  private _render = debounce(() => {
@@ -184,10 +217,21 @@ export class Ele extends UiBase<Attrs, Emits> {
184
217
  this._calendar.timeEnd =
185
218
  +currentTime;
186
219
  this._calendar.showingTime = this.showingTime;
220
+
221
+ const selectorWrapper =
222
+ this.shadowRoot!.querySelector<HTMLElement>('.time-selector')!;
223
+ if (this.minGranularity === 'day') {
224
+ this._timeSelector.millisecond = 0;
225
+ selectorWrapper.style.display = 'none';
226
+ return;
227
+ }
228
+ selectorWrapper.style.display = '';
229
+ this._timeSelector.minGranularity = this.minGranularity;
230
+
187
231
  this._timeSelector.millisecond =
188
232
  (+currentTime - tz) % (24 * 60 * 60 * 1000);
189
233
  this.shadowRoot!.querySelector('.wrapper .time-echo')!.textContent =
190
- this.timeFormatter(currentTime as Date);
234
+ this.timeFormatter(currentTime as Date, this.minGranularity);
191
235
  }, 0);
192
236
 
193
237
  private _onCalendarSelect = (e: CalendarBaseEvent['select-time']) => {
@@ -217,7 +261,6 @@ export class Ele extends UiBase<Attrs, Emits> {
217
261
  private _onTimeSelectorDoneClick = (e: Event) => {
218
262
  const btn = closestByEvent(e, '#time-selector-done-btn');
219
263
  if (!btn) return;
220
- const type = btn.dataset.type;
221
264
  const calcTime = (time: Date, ms: number) => {
222
265
  time.setHours(0, 0, 0, 0);
223
266
  time.setMilliseconds(ms);
@@ -230,10 +273,19 @@ export class Ele extends UiBase<Attrs, Emits> {
230
273
  this._timePopover.open = false;
231
274
  };
232
275
 
233
- public timeFormatter = (time: Date) =>
234
- new Date(+time - new Date().getTimezoneOffset() * 60 * 1000)
276
+ public timeFormatter = (
277
+ time: Date,
278
+ minGranularity: 'day' | 'hour' | 'minute' | 'second' | 'millisecond'
279
+ ) => {
280
+ const t = new Date(+time - new Date().getTimezoneOffset() * 60 * 1000)
235
281
  .toISOString()
236
282
  .slice(11, 23);
283
+ if (minGranularity === 'day') return '';
284
+ if (minGranularity === 'hour') return t.slice(0, 2);
285
+ if (minGranularity === 'minute') return t.slice(0, 5);
286
+ if (minGranularity === 'second') return t.slice(0, 8);
287
+ return t;
288
+ };
237
289
  }
238
290
 
239
291
  Ele.define();
@@ -10,7 +10,7 @@ dt-popover {
10
10
  position: relative;
11
11
  }
12
12
 
13
- [open] .time-echo-wrapper {
13
+ [open] > .time-echo-wrapper {
14
14
  border-color: #18181B;
15
15
  }
16
16
 
@@ -83,4 +83,43 @@ dt-hhmmss-ms-list-grp::part(item) {
83
83
  line-height: 1;
84
84
  }
85
85
 
86
+ .menu {
87
+ display: flex;
88
+ flex-direction: column;
89
+ align-items: center;
90
+ padding: 10px 5px;
91
+ font-size: 14px;
92
+ gap: 10px;
93
+ border-radius: 6px;
94
+ border: 1px solid #eee;
95
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
96
+ background-color: #fff;
97
+ width: 285px;
98
+
99
+ & > * {
100
+ width: 100%;
101
+ box-sizing: border-box;
102
+ }
103
+ }
104
+
105
+ dt-calendar-base {
106
+ // 254 = item height 6 * 30 + week 14 + gap 10 * 6
107
+ height: 254px;
108
+
109
+ &::part(week) {
110
+ font-size: 12px;
111
+ line-height: 14px;
112
+ }
113
+
114
+ &::part(item) {
115
+ font-size: 14px;
116
+ }
117
+ }
118
+ dt-yyyymm-nav::part(list-grp) {
119
+ height: 254px;
120
+ margin-top: 10px;
121
+ }
122
+ dt-calendar-base.hide {
123
+ display: none;
124
+ }
86
125
  `;
@@ -114,7 +114,7 @@ export class Ele extends UiBase<Attrs, Emits> {
114
114
  protected _style = styleStr;
115
115
  protected _template = html`
116
116
  <div class="cols" part="cols">
117
- <div class="col" part="col hour">
117
+ <div class="col hour" part="col hour">
118
118
  <span>Hour</span>
119
119
  <dt-num-list
120
120
  exportparts="container:list-container, item, item-current"
@@ -124,7 +124,7 @@ export class Ele extends UiBase<Attrs, Emits> {
124
124
  max-num="23"
125
125
  ></dt-num-list>
126
126
  </div>
127
- <div class="col" part="col minute">
127
+ <div class="col minute" part="col minute">
128
128
  <span>Minute</span>
129
129
  <dt-num-list
130
130
  exportparts="container:list-container, item, item-current"
@@ -134,7 +134,7 @@ export class Ele extends UiBase<Attrs, Emits> {
134
134
  max-num="59"
135
135
  ></dt-num-list>
136
136
  </div>
137
- <div class="col" part="col second">
137
+ <div class="col second" part="col second">
138
138
  <span>Second</span>
139
139
  <dt-num-list
140
140
  exportparts="container:list-container, item, item-current"
@@ -156,14 +156,18 @@ export class Ele extends UiBase<Attrs, Emits> {
156
156
  this._applyTemplate();
157
157
  }
158
158
 
159
- private get _colsHourEle() {
160
- return this.shadowRoot?.querySelector('.cols .hour') as NumListEle;
159
+ private get _listEleHour() {
160
+ return this.shadowRoot?.querySelector('dt-num-list.hour') as NumListEle;
161
161
  }
162
- private get _colsMinuteEle() {
163
- return this.shadowRoot?.querySelector('.cols .minute') as NumListEle;
162
+ private get _listEleMinute() {
163
+ return this.shadowRoot?.querySelector(
164
+ 'dt-num-list.minute'
165
+ ) as NumListEle;
164
166
  }
165
- private get _colsSecondEle() {
166
- return this.shadowRoot?.querySelector('.cols .second') as NumListEle;
167
+ private get _listEleSecond() {
168
+ return this.shadowRoot?.querySelector(
169
+ 'dt-num-list.second'
170
+ ) as NumListEle;
167
171
  }
168
172
  private get _msInputEle() {
169
173
  return this.shadowRoot?.querySelector('input#ms') as HTMLInputElement;
@@ -178,35 +182,21 @@ export class Ele extends UiBase<Attrs, Emits> {
178
182
  this.setAttribute('millisecond', '' + v);
179
183
  }
180
184
  public get maxGranularity() {
181
- return this._getAttr('max-granularity', 'hour') as
182
- | 'hour'
183
- | 'minute'
184
- | 'second'
185
- | 'millisecond';
185
+ return this._getAttr('max-granularity', 'hour');
186
186
  }
187
187
  public set maxGranularity(v: 'hour' | 'minute' | 'second' | 'millisecond') {
188
188
  if (!['hour', 'minute', 'second', 'millisecond'].includes(v)) return;
189
189
  this.setAttribute('max-granularity', v);
190
190
  }
191
191
  public get minGranularity() {
192
- return this._getAttr('min-granularity', 'millisecond') as
193
- | 'hour'
194
- | 'minute'
195
- | 'second'
196
- | 'millisecond';
192
+ return this._getAttr('min-granularity', 'millisecond');
197
193
  }
198
194
  public set minGranularity(v: 'hour' | 'minute' | 'second' | 'millisecond') {
199
195
  if (!['hour', 'minute', 'second', 'millisecond'].includes(v)) return;
200
196
  this.setAttribute('min-granularity', v);
201
197
  }
202
198
  public get colOrder() {
203
- return this._getAttr('col-order', 'smh') as
204
- | 'hms'
205
- | 'hsm'
206
- | 'mhs'
207
- | 'msh'
208
- | 'shm'
209
- | 'smh';
199
+ return this._getAttr('col-order', 'smh');
210
200
  }
211
201
  public set colOrder(v: 'hms' | 'hsm' | 'mhs' | 'msh' | 'shm' | 'smh') {
212
202
  if (!['hms', 'hsm', 'mhs', 'msh', 'shm', 'smh'].includes(v)) return;
@@ -221,28 +211,28 @@ export class Ele extends UiBase<Attrs, Emits> {
221
211
 
222
212
  public connectedCallback() {
223
213
  if (!super.connectedCallback()) return;
224
- this._colsHourEle.formatter =
225
- this._colsMinuteEle.formatter =
226
- this._colsSecondEle.formatter =
214
+ this._listEleHour.formatter =
215
+ this._listEleMinute.formatter =
216
+ this._listEleSecond.formatter =
227
217
  (num) => ('0' + num).slice(-2);
228
218
 
229
219
  this._renderCols();
230
220
  this._updateGranularity();
231
221
  this._updateColsValue();
232
222
 
233
- this._colsHourEle.addEventListener('select-num', this._onColsSelect);
234
- this._colsMinuteEle.addEventListener('select-num', this._onColsSelect);
235
- this._colsSecondEle.addEventListener('select-num', this._onColsSelect);
223
+ this._listEleHour.addEventListener('select-num', this._onColsSelect);
224
+ this._listEleMinute.addEventListener('select-num', this._onColsSelect);
225
+ this._listEleSecond.addEventListener('select-num', this._onColsSelect);
236
226
  this._msInputEle.addEventListener('input', this._onMsInput);
237
227
  }
238
228
  public disconnectedCallback() {
239
229
  if (!super.disconnectedCallback()) return;
240
- this._colsHourEle.removeEventListener('select-num', this._onColsSelect);
241
- this._colsMinuteEle.removeEventListener(
230
+ this._listEleHour.removeEventListener('select-num', this._onColsSelect);
231
+ this._listEleMinute.removeEventListener(
242
232
  'select-num',
243
233
  this._onColsSelect
244
234
  );
245
- this._colsSecondEle.removeEventListener(
235
+ this._listEleSecond.removeEventListener(
246
236
  'select-num',
247
237
  this._onColsSelect
248
238
  );
@@ -261,9 +251,9 @@ export class Ele extends UiBase<Attrs, Emits> {
261
251
  if (!this.isConnected) return;
262
252
  const {
263
253
  colOrder,
264
- _colsHourEle: hEle,
265
- _colsMinuteEle: mEle,
266
- _colsSecondEle: sEle
254
+ _listEleHour: hEle,
255
+ _listEleMinute: mEle,
256
+ _listEleSecond: sEle
267
257
  } = this;
268
258
  // columns order
269
259
  const orderedCols: HTMLElement[] = [];
@@ -283,13 +273,15 @@ export class Ele extends UiBase<Attrs, Emits> {
283
273
 
284
274
  private _updateGranularity = debounce(() => {
285
275
  if (!this.isConnected) return;
286
- const {
287
- _colsHourEle: hEle,
288
- _colsMinuteEle: mEle,
289
- _colsSecondEle: sEle,
290
- maxGranularity,
291
- minGranularity
292
- } = this;
276
+ const { maxGranularity, minGranularity } = this;
277
+ const hEle = this.shadowRoot!.querySelector<HTMLElement>('.col.hour')!;
278
+ const mEle =
279
+ this.shadowRoot!.querySelector<HTMLElement>('.col.minute')!;
280
+ const sEle =
281
+ this.shadowRoot!.querySelector<HTMLElement>('.col.second')!;
282
+ const msEle = this.shadowRoot!.querySelector<HTMLElement>(
283
+ '[part="ms-wrapper"]'
284
+ )!;
293
285
  const colsContainer =
294
286
  this.shadowRoot!.querySelector<HTMLElement>('.cols')!;
295
287
 
@@ -311,17 +303,15 @@ export class Ele extends UiBase<Attrs, Emits> {
311
303
  ).length
312
304
  ? ''
313
305
  : 'none';
314
- this.shadowRoot!.querySelector<HTMLElement>(
315
- '[part="ms-wrapper"]'
316
- )!.style.display = maxG >= 0 && minG <= 0 ? '' : 'none';
306
+ msEle.style.display = maxG >= 0 && minG <= 0 ? '' : 'none';
317
307
  }, 0);
318
308
 
319
309
  private _updateColsValue = debounce(() => {
320
310
  if (!this.isConnected) return;
321
311
  const {
322
- _colsHourEle: hEle,
323
- _colsMinuteEle: mEle,
324
- _colsSecondEle: sEle,
312
+ _listEleHour: hEle,
313
+ _listEleMinute: mEle,
314
+ _listEleSecond: sEle,
325
315
  millisecond
326
316
  } = this;
327
317
 
@@ -339,9 +329,9 @@ export class Ele extends UiBase<Attrs, Emits> {
339
329
  }, 0);
340
330
 
341
331
  private _getMsFromEle() {
342
- const hour = this._colsHourEle.currentNum;
343
- const minute = this._colsMinuteEle.currentNum;
344
- const second = this._colsSecondEle.currentNum;
332
+ const hour = this._listEleHour.currentNum;
333
+ const minute = this._listEleMinute.currentNum;
334
+ const second = this._listEleSecond.currentNum;
345
335
  const ms = Math.min(Math.max(0, +this._msInputEle.value || 0), 999);
346
336
  return ((hour * 60 + minute) * 60 + second) * 1000 + ms;
347
337
  }
@@ -126,33 +126,21 @@ export class Ele extends UiBase<Attrs, Emits> {
126
126
  this.setAttribute('millisecond', '' + Math.floor(v));
127
127
  }
128
128
  public get maxGranularity() {
129
- return this._getAttr('max-granularity', 'year') as
130
- | 'year'
131
- | 'month'
132
- | 'day';
129
+ return this._getAttr('max-granularity', 'year');
133
130
  }
134
131
  public set maxGranularity(v: 'year' | 'month' | 'day') {
135
132
  if (!['year', 'month', 'day'].includes(v)) return;
136
133
  this.setAttribute('max-granularity', v);
137
134
  }
138
135
  public get minGranularity() {
139
- return this._getAttr('min-granularity', 'day') as
140
- | 'year'
141
- | 'month'
142
- | 'day';
136
+ return this._getAttr('min-granularity', 'day');
143
137
  }
144
138
  public set minGranularity(v: 'year' | 'month' | 'day') {
145
139
  if (!['year', 'month', 'day'].includes(v)) return;
146
140
  this.setAttribute('min-granularity', v);
147
141
  }
148
142
  public get colOrder() {
149
- return this._getAttr('col-order', 'dmy') as
150
- | 'ymd'
151
- | 'ydm'
152
- | 'myd'
153
- | 'mdy'
154
- | 'dym'
155
- | 'dmy';
143
+ return this._getAttr('col-order', 'dmy');
156
144
  }
157
145
  public set colOrder(v: 'ymd' | 'ydm' | 'myd' | 'mdy' | 'dym' | 'dmy') {
158
146
  if (!['ymd', 'ydm', 'myd', 'mdy', 'dym', 'dmy'].includes(v)) return;