@poirazis/supercomponents-shared 1.2.16 → 1.2.18

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.
Files changed (31) hide show
  1. package/dist/index.js +17703 -22317
  2. package/dist/index.umd.cjs +19 -19
  3. package/package.json +11 -11
  4. package/src/lib/SuperButton/SuperButton.svelte +57 -22
  5. package/src/lib/SuperField/SuperField.svelte +0 -2
  6. package/src/lib/SuperForm/InnerForm.svelte +7 -8
  7. package/src/lib/SuperTable/SuperTable.css +13 -6
  8. package/src/lib/SuperTable/SuperTable.svelte +26 -15
  9. package/src/lib/SuperTable/constants.js +1 -1
  10. package/src/lib/SuperTable/controls/RowButtonsColumn.svelte +33 -17
  11. package/src/lib/SuperTable/controls/SelectionColumn.svelte +2 -2
  12. package/src/lib/SuperTableCells/CellBoolean.svelte +52 -45
  13. package/src/lib/SuperTableCells/CellDatetime.svelte +268 -120
  14. package/src/lib/SuperTableCells/CellLink.svelte +16 -7
  15. package/src/lib/SuperTableCells/CellLinkPickerSelect.svelte +28 -24
  16. package/src/lib/SuperTableCells/CellLinkPickerTree.svelte +22 -20
  17. package/src/lib/SuperTableCells/CellNumber.svelte +55 -53
  18. package/src/lib/SuperTableCells/CellOptions.svelte +59 -32
  19. package/src/lib/SuperTableCells/CellOptionsAdvanced.svelte +131 -129
  20. package/src/lib/SuperTableCells/CellSQLLink.svelte +32 -32
  21. package/src/lib/SuperTableCells/CellSQLLinkPicker.svelte +2 -7
  22. package/src/lib/SuperTableCells/CellString.svelte +49 -47
  23. package/src/lib/SuperTableCells/CellStringMask.svelte +41 -40
  24. package/src/lib/SuperTableColumn/SuperTableColumn.svelte +2 -0
  25. package/src/lib/SuperTableColumn/parts/SuperColumnBody.svelte +3 -0
  26. package/src/lib/SuperTableColumn/parts/SuperColumnHeader.svelte +44 -42
  27. package/src/lib/SuperTableColumn/parts/SuperColumnRow.svelte +1 -1
  28. package/src/lib/SuperTabs/SuperTabs.svelte +33 -17
  29. package/src/lib/UI/elements/Checkbox.svelte +68 -10
  30. package/src/lib/UI/elements/Switch.svelte +162 -0
  31. package/src/lib/UI/elements/Tooltip.svelte +15 -43
@@ -1,6 +1,8 @@
1
1
  <script>
2
- import { createEventDispatcher, getContext } from "svelte";
2
+ import { createEventDispatcher } from "svelte";
3
3
  import fsm from "svelte-fsm";
4
+ import Switch from "../UI/elements/Switch.svelte";
5
+ import Checkbox from "../UI/elements/Checkbox.svelte";
4
6
  import "./CellCommon.css";
5
7
 
6
8
  export let value;
@@ -21,20 +23,20 @@
21
23
  View: {
22
24
  focus() {
23
25
  if (!cellOptions.readonly && !cellOptions.disabled) return "Editing";
24
- editor?.focus();
25
26
  },
26
27
  },
27
28
  Editing: {
28
29
  _enter() {
29
30
  originalValue = value;
30
- editor?.focus();
31
31
  dispatch("enteredit");
32
32
  },
33
33
  _exit() {
34
34
  dispatch("exitedit");
35
35
  },
36
- focus() {
37
- editor?.focus();
36
+ focus() {},
37
+ toggle() {
38
+ value = !value;
39
+ dispatch("change", value);
38
40
  },
39
41
  change(e) {
40
42
  if (cellOptions.debounce) dispatch("change", value);
@@ -56,15 +58,10 @@
56
58
  $: inline = cellOptions.role == "inlineInput";
57
59
  $: inEdit = $cellState == "Editing";
58
60
  $: isDirty = inEdit && originalValue !== value;
59
- $: checkbox = cellOptions?.controlType == "checkbox";
60
61
  $: tableCell = cellOptions.role == "tableCell";
61
62
  $: inlineLabel = cellOptions.inlineLabel;
62
63
  $: icon = cellOptions.error ? "ri-error-warning-line" : cellOptions.icon;
63
64
  $: error = cellOptions.error;
64
-
65
- const focus = (node) => {
66
- if (cellOptions.role == "tableCell") node.focus();
67
- };
68
65
  </script>
69
66
 
70
67
  <!-- svelte-ignore a11y-click-events-have-key-events -->
@@ -72,6 +69,7 @@
72
69
  <!-- svelte-ignore a11y-no-static-element-interactions -->
73
70
  <div
74
71
  class="superCell"
72
+ tabindex={cellOptions.disabled || cellOptions.readonly ? undefined : "1"}
75
73
  class:inEdit
76
74
  class:isDirty={isDirty && cellOptions.showDirty}
77
75
  class:inline
@@ -84,6 +82,11 @@
84
82
  ? "var(--spectrum-global-color-gray-50)"
85
83
  : cellOptions.background}
86
84
  style:font-weight={cellOptions.fontWeight}
85
+ on:keydown={(e) => {
86
+ if (e.code == "Space") cellState.toggle();
87
+ }}
88
+ on:focusin={cellState.focus}
89
+ on:focusout={cellState.submit}
87
90
  >
88
91
  {#if icon}
89
92
  <i class={icon + " field-icon"} class:with-error={error}></i>
@@ -95,48 +98,40 @@
95
98
  class:naked-field={!tableCell}
96
99
  style:justify-content={cellOptions.align ?? "center"}
97
100
  >
98
- {#if !checkbox}
99
- <div class="spectrum-Switch spectrum-Switch--emphasized">
100
- <input
101
- class="spectrum-Switch-input"
102
- bind:checked={value}
103
- bind:this={editor}
104
- type="checkbox"
101
+ {#if cellOptions.controlType == "switch"}
102
+ <div class="switch-wrapper">
103
+ <Switch
104
+ checked={value}
105
105
  disabled={cellOptions.disabled || cellOptions.readonly}
106
- on:focusin={cellState.focus}
107
- on:focusout={cellState.submit}
108
- on:change={cellState.change}
109
- use:focus
106
+ size="medium"
107
+ on:change={(e) => {
108
+ value = e.detail.checked;
109
+ cellState.change();
110
+ }}
110
111
  />
111
- <span class="spectrum-Switch-switch"></span>
112
112
  {#if inlineLabel}
113
- <span class="spectrum-Switch-label">{inlineLabel}</span>
113
+ <span class="switch-label">{inlineLabel}</span>
114
114
  {/if}
115
115
  </div>
116
116
  {:else}
117
- <input
118
- class="checkbox"
119
- bind:checked={value}
120
- bind:this={editor}
121
- type="checkbox"
122
- disabled={cellOptions.disabled || cellOptions.readonly}
123
- on:focusin={cellState.focus}
124
- on:focusout={cellState.submit}
125
- on:change={cellState.change}
126
- use:focus
127
- />
128
- {#if inlineLabel}
129
- <span class="checkbox-label">{inlineLabel}</span>
130
- {/if}
117
+ <div class="checkbox-wrapper">
118
+ <Checkbox
119
+ checked={value}
120
+ size="medium"
121
+ disabled={cellOptions.disabled || cellOptions.readonly}
122
+ on:change={(e) => {
123
+ value = e.detail.checked;
124
+ cellState.change();
125
+ }}
126
+ />
127
+ {#if inlineLabel}
128
+ <span class="checkbox-label">{inlineLabel}</span>
129
+ {/if}
130
+ </div>
131
131
  {/if}
132
132
  </div>
133
133
  {:else}
134
- <div
135
- class="value"
136
- tabIndex="0"
137
- style:justify-content={cellOptions.align ?? "center"}
138
- on:focusin={cellState.focus}
139
- >
134
+ <div class="value" style:justify-content={cellOptions.align ?? "center"}>
140
135
  {#if value}
141
136
  <i class="ri-check-line valueicon"></i>
142
137
  {:else if cellOptions.showFalse}
@@ -147,8 +142,20 @@
147
142
  </div>
148
143
 
149
144
  <style>
150
- .spectrum-Switch {
151
- margin-left: 0.25rem !important;
145
+ .switch-wrapper {
146
+ display: flex;
147
+ align-items: center;
148
+ gap: 0.5rem;
149
+ }
150
+
151
+ .checkbox-wrapper {
152
+ display: flex;
153
+ align-items: center;
154
+ gap: 0.5rem;
155
+ }
156
+
157
+ .switch-label {
158
+ margin-left: 0.25rem;
152
159
  }
153
160
 
154
161
  .checkbox-label {
@@ -12,6 +12,11 @@
12
12
  export let autofocus;
13
13
 
14
14
  let originalValue;
15
+ let anchor;
16
+ let picker;
17
+ let timePicker;
18
+ let open;
19
+ let selection = false;
15
20
 
16
21
  export let cellState = fsm("View", {
17
22
  "*": {
@@ -20,6 +25,10 @@
20
25
  },
21
26
  },
22
27
  View: {
28
+ _enter() {
29
+ open = false;
30
+ selection = false;
31
+ },
23
32
  focus() {
24
33
  if (!cellOptions.readonly) return "Editing";
25
34
  },
@@ -33,125 +42,176 @@
33
42
  _exit() {
34
43
  dispatch("exitedit");
35
44
  },
45
+ toggle(e) {
46
+ open = !open;
47
+ },
36
48
  handleKeyboard(e) {
37
49
  if (e.keyCode == 32) {
38
50
  e.stopPropagation();
39
51
  e.preventDefault();
40
52
  open = !open;
41
53
  }
54
+
55
+ if (e.code == "Delete" || e.code == "Backspace") {
56
+ e.stopPropagation();
57
+ e.preventDefault();
58
+ value = null;
59
+ dispatch("change", value);
60
+ }
42
61
  },
43
62
  focusout(e) {
44
63
  const isInPicker = picker?.contains(e.relatedTarget);
45
64
  const isInTimePicker = timePicker?.contains(e.relatedTarget);
46
65
 
66
+ if (isInPicker || isInTimePicker) return;
67
+
47
68
  if (!isInPicker && !isInTimePicker) {
48
69
  open = false;
49
- if (value != originalValue) {
50
- dispatch("change", value);
70
+ let outputValue = innerDate.toISOString();
71
+ if (!currentShowTime) {
72
+ outputValue = innerDate.toLocaleDateString("en-CA"); // YYYY-MM-DD format without time
51
73
  }
74
+
75
+ if (ignoreTimeZone) {
76
+ outputValue = innerDate.toISOString().slice(0, -1);
77
+ }
78
+
79
+ if (selection) dispatch("change", outputValue);
52
80
  return "View";
53
81
  }
54
82
  },
83
+ submit() {
84
+ dispatch("change", innerDate.toISOString());
85
+ return "View";
86
+ },
55
87
  cancel() {
56
- value = Array.isArray(originalValue)
57
- ? [...originalValue]
58
- : originalValue;
88
+ value = originalValue;
59
89
  return "View";
60
90
  },
61
91
  },
62
92
  });
63
93
 
64
- let anchor;
65
- let picker;
66
- let timePicker;
67
- let open;
68
-
69
94
  // Current date format and showTime - extracted to ensure reactivity
70
95
  $: currentDateFormat = cellOptions?.dateFormat;
71
96
  $: currentShowTime = cellOptions?.showTime;
97
+ $: ignoreTimeZone = cellOptions?.ignoreTimezone;
98
+ $: show24HTime = cellOptions?.show24HTime;
99
+ $: innerDate = parseValueToDate(value);
100
+ $: timeValue = innerDate.toLocaleTimeString("en-US", {
101
+ hour12: !show24HTime,
102
+ hour: "2-digit",
103
+ minute: "2-digit",
104
+ });
72
105
 
73
- // Time value (format: HH:mm)
74
- $: timeValue = currentShowTime && value ? getTimeFromValue() : "00:00";
75
- $: innerDate = value ? new Date(value) : new Date();
106
+ // Helper function to create a Date object that properly handles UTC when ignoreTimezone is true
107
+ function parseValueToDate(valueStr) {
108
+ if (!valueStr) return new Date();
109
+ if (valueStr instanceof Date) {
110
+ valueStr = valueStr;
111
+ }
76
112
 
77
- // Extract time from current value if it exists
78
- function getTimeFromValue() {
79
- if (!value) return "00:00";
80
- const date = new Date(value);
81
- return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
113
+ return new Date(valueStr);
82
114
  }
83
115
 
84
- // Date formatting helper function
85
- function formatDate(date, dateFormat) {
86
- if (!date) return "";
87
-
88
- if (!dateFormat || dateFormat === "default") {
89
- return date?.toDateString();
90
- }
116
+ // Parse 12-hour time format to 24-hour format
117
+ function parse12HourTime(timeStr) {
118
+ const match = timeStr.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i);
119
+ if (!match) return null;
120
+ let hours = parseInt(match[1]);
121
+ const minutes = parseInt(match[2]);
122
+ const ampm = match[3].toUpperCase();
91
123
 
92
- // Manual formatting for specific formats to ensure exact order
93
- if (dateFormat === "MM/DD/YYYY") {
94
- return `${(date.getMonth() + 1).toString().padStart(2, "0")}/${date.getDate().toString().padStart(2, "0")}/${date.getFullYear()}`;
95
- }
124
+ if (ampm === "PM" && hours !== 12) hours += 12;
125
+ if (ampm === "AM" && hours === 12) hours = 0;
96
126
 
97
- if (dateFormat === "DD/MM/YYYY") {
98
- return `${date.getDate().toString().padStart(2, "0")}/${(date.getMonth() + 1).toString().padStart(2, "0")}/${date.getFullYear()}`;
99
- }
127
+ return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
128
+ }
100
129
 
101
- if (dateFormat === "YYYY-MM-DD") {
102
- return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
103
- }
130
+ // Format date/datetime helper function
131
+ // Only applies formatting - timezone handling is done in parseValueToDate
132
+ // If showTime is false (default), returns formatted date only
133
+ // If showTime is true, returns formatted date + time
134
+ function formatDateTime(date, dateFormat, showTime = false) {
135
+ if (!date) return "";
104
136
 
105
- // For shorter formats, still use locale-specific formatting
106
- const options = {
107
- "MMM DD, YYYY": {
108
- month: "short",
109
- day: "numeric",
110
- year: "numeric",
111
- timeZone: "UTC",
112
- },
113
- "DD MMM YYYY": {
114
- day: "numeric",
115
- month: "short",
116
- year: "numeric",
117
- timeZone: "UTC",
118
- },
119
- };
137
+ // Format the date part
138
+ let dateResult = "";
120
139
 
121
- const formatOption = options[dateFormat];
122
- if (formatOption) {
123
- return date.toLocaleDateString("en-US", formatOption);
140
+ if (!dateFormat || dateFormat === "default") {
141
+ dateResult = date?.toDateString();
142
+ } else {
143
+ // Use local methods to format - timezone is already handled by parseValueToDate
144
+ const month = date.getMonth();
145
+ const dayOfMonth = date.getDate();
146
+ const year = date.getFullYear();
147
+
148
+ // Manual formatting for specific formats to ensure exact order
149
+ if (dateFormat === "MM/DD/YYYY") {
150
+ dateResult = `${(month + 1).toString().padStart(2, "0")}/${dayOfMonth.toString().padStart(2, "0")}/${year}`;
151
+ } else if (dateFormat === "DD/MM/YYYY") {
152
+ dateResult = `${dayOfMonth.toString().padStart(2, "0")}/${(month + 1).toString().padStart(2, "0")}/${year}`;
153
+ } else if (dateFormat === "YYYY-MM-DD") {
154
+ dateResult = `${year}-${(month + 1).toString().padStart(2, "0")}-${dayOfMonth.toString().padStart(2, "0")}`;
155
+ } else {
156
+ // For shorter formats, use locale-specific formatting
157
+ const options = {
158
+ "MMM DD, YYYY": {
159
+ month: "short",
160
+ day: "numeric",
161
+ year: "numeric",
162
+ },
163
+ "DD MMM YYYY": {
164
+ day: "numeric",
165
+ month: "short",
166
+ year: "numeric",
167
+ },
168
+ };
169
+
170
+ const formatOption = options[dateFormat];
171
+ if (formatOption) {
172
+ dateResult = date.toLocaleDateString("en-US", formatOption);
173
+ } else {
174
+ // Fallback to default
175
+ dateResult = date?.toDateString();
176
+ }
177
+ }
124
178
  }
125
179
 
126
- // Fallback to default
127
- return date?.toDateString();
128
- }
129
-
130
- // DateTime formatting helper function
131
- function formatDateTime(date, dateFormat, showTime) {
132
- if (!date) return "";
133
-
180
+ // If not showing time, return just the formatted date
134
181
  if (!showTime) {
135
- return formatDate(date, dateFormat);
182
+ return dateResult;
136
183
  }
137
184
 
138
- const formattedDate = formatDate(date, dateFormat);
139
- const timeString = `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
185
+ // Format the time part when showTime is true (using local time)
186
+ let hours = date.getHours();
187
+ let minutes = date.getMinutes();
188
+
189
+ // Format based on show24HTime
190
+ let timeString;
191
+ if (show24HTime) {
192
+ timeString = `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
193
+ } else {
194
+ const ampm = hours >= 12 ? "PM" : "AM";
195
+ const display12h = hours % 12 || 12;
196
+ timeString = `${display12h.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")} ${ampm}`;
197
+ }
140
198
 
141
- return `${formattedDate} ${timeString}`;
199
+ return `${dateResult} ${timeString}`;
142
200
  }
143
201
 
144
202
  $: formattedValue =
145
- cellOptions.template && value
146
- ? processStringSync(cellOptions.template, { value })
147
- : value
148
- ? formatDate(innerDate, currentDateFormat)
149
- : undefined;
203
+ cellOptions.template && value && value != "" && value != null
204
+ ? processStringSync(cellOptions.template, {
205
+ value: innerDate,
206
+ })
207
+ : (value && value != "" && value != null) || selection
208
+ ? formatDateTime(innerDate, currentDateFormat, currentShowTime)
209
+ : "";
150
210
 
151
211
  $: placeholder = cellOptions?.placeholder || "";
152
212
  $: inEdit = $cellState == "Editing";
153
213
  $: inline = cellOptions.role == "inlineInpur";
154
- $: isDirty = inEdit && originalValue != value;
214
+ $: isDirty = inEdit && true;
155
215
 
156
216
  onMount(() => {
157
217
  if (autofocus)
@@ -161,20 +221,72 @@
161
221
  }, 30);
162
222
  });
163
223
 
164
- // Handle time picker changes
224
+ // Handle Start button - set time to beginning of day (00:00 or 12:00 AM)
225
+ const handleStartTime = () => {
226
+ const startTime = show24HTime ? "00:00" : "12:00 AM";
227
+ timeValue = startTime;
228
+
229
+ innerDate.setHours(0, 0, 0, 0);
230
+ innerDate = innerDate;
231
+ selection = true;
232
+ anchor?.focus();
233
+ };
234
+
235
+ // Handle End button - set time to end of day (23:59 or 11:59 PM)
236
+ const handleEndTime = () => {
237
+ const endTime = show24HTime ? "23:59" : "11:59 PM";
238
+ timeValue = endTime;
239
+
240
+ innerDate.setHours(23, 59, 0, 0);
241
+ innerDate = innerDate;
242
+ selection = true;
243
+ anchor?.focus();
244
+ };
245
+
246
+ // Handle Now button - set both date and time to current date/time
247
+ const handleNowTime = () => {
248
+ innerDate = new Date();
249
+ selection = true;
250
+ anchor?.focus();
251
+ };
252
+
253
+ // Handle Clear button - clear the value
254
+ const handleClearValue = () => {
255
+ cellState.submit();
256
+ };
257
+
258
+ // Handle time picker changes - only affects the time, not timezone
165
259
  const handleTimeChange = (e) => {
166
- if (!value || !currentShowTime) return;
260
+ if (!currentShowTime) return;
261
+ selection = true;
167
262
 
168
263
  const newTime = e.target.value;
169
- const currentDate = new Date(value);
170
- const [hours, minutes] = newTime.split(":").map(Number);
264
+ // Convert 12-hour format to 24-hour if needed
265
+ const time24h = show24HTime ? newTime : parse12HourTime(newTime);
266
+ if (!time24h) return;
267
+
268
+ const [hours, minutes] = time24h.split(":").map(Number);
269
+
270
+ innerDate.setHours(hours, minutes, 0, 0);
271
+ };
171
272
 
172
- // Update the date with the new time
173
- currentDate.setHours(hours, minutes, 0, 0);
174
- value = currentDate.toISOString();
273
+ const handleDateChange = (e) => {
274
+ const newDate = e.detail;
275
+ selection = true;
276
+
277
+ if (currentShowTime) {
278
+ // Preserve the time from the current innerDate when appending to the selected date
279
+ newDate.setHours(
280
+ innerDate.getHours(),
281
+ innerDate.getMinutes(),
282
+ innerDate.getSeconds(),
283
+ innerDate.getMilliseconds(),
284
+ );
285
+ }
175
286
 
176
- // Keep the popover open for time changes
177
- dispatch("change", value);
287
+ innerDate = newDate;
288
+ anchor?.focus();
289
+ open = false;
178
290
  };
179
291
  </script>
180
292
 
@@ -197,45 +309,45 @@
197
309
  style:color={cellOptions.color}
198
310
  style:background={cellOptions.background}
199
311
  on:focus={cellState.focus}
200
- on:keypress={cellState.handleKeyboard}
312
+ on:keydown={cellState.handleKeyboard}
201
313
  on:focusout={cellState.focusout}
314
+ on:mousedown={cellState.toggle}
202
315
  >
203
- {#if cellOptions.icon}
204
- <i class={cellOptions.icon + " field-icon"}></i>
205
- {/if}
206
-
207
- {#if inEdit}
208
- <div
209
- class="editor"
210
- class:placeholder={!formattedValue}
211
- on:click={() => (open = !open)}
212
- >
213
- <span>{formattedValue || placeholder}</span>
214
- <i
215
- class="ri-calendar-line"
216
- style="font-size: 16px; justify-self: flex-end"
217
- ></i>
218
- </div>
219
- {:else}
220
- <div
221
- class="value"
222
- class:placeholder={!formattedValue}
223
- style:justify-content={cellOptions.align}
224
- >
225
- <span>
226
- {formattedValue || placeholder}
227
- </span>
228
- </div>
229
- {/if}
316
+ {#key $cellState}
317
+ {#if cellOptions.icon}
318
+ <i class={cellOptions.icon + " field-icon"}></i>
319
+ {/if}
320
+
321
+ {#if inEdit}
322
+ <div class="editor" class:placeholder={!formattedValue}>
323
+ <span>{formattedValue || placeholder}</span>
324
+ <i
325
+ class="ri-calendar-line"
326
+ style="font-size: 16px; justify-self: flex-end"
327
+ ></i>
328
+ </div>
329
+ {:else}
330
+ <div
331
+ class="value"
332
+ class:placeholder={!formattedValue}
333
+ style:justify-content={cellOptions.align}
334
+ >
335
+ <span>
336
+ {value ? formattedValue : placeholder}
337
+ </span>
338
+ </div>
339
+ {/if}
340
+ {/key}
230
341
  </div>
231
342
 
232
343
  {#if inEdit}
233
344
  <SuperPopover
234
345
  {anchor}
235
- dismissible={false}
236
346
  {open}
347
+ ignoreAnchor
237
348
  align="right"
238
349
  maxHeight={400}
350
+ on:close={cellState.focusout}
239
351
  >
240
352
  <div
241
353
  bind:this={picker}
@@ -244,26 +356,34 @@
244
356
  style:--date-picker-foreground="var(--spectrum-global-color-gray-800)"
245
357
  style:--date-picker-selected-background="var(--accent-color)"
246
358
  >
247
- <DatePicker
248
- bind:value={innerDate}
249
- on:select={(e) => {
250
- value = e.detail;
251
- anchor?.focus();
252
- open = false;
253
- }}
254
- />
359
+ <DatePicker value={innerDate} on:select={handleDateChange} />
255
360
 
256
361
  {#if currentShowTime}
257
362
  <div class="time-section">
258
363
  <!-- svelte-ignore a11y-label-has-associated-control -->
259
364
  <input
260
365
  bind:this={timePicker}
261
- type="time"
366
+ type="text"
367
+ placeholder={show24HTime ? "HH:MM" : "HH:MM AM/PM"}
262
368
  bind:value={timeValue}
263
369
  on:change={handleTimeChange}
370
+ on:focusout={cellState.focusout}
264
371
  class="time-input"
265
- step="900"
266
372
  />
373
+ <div class="time-buttons">
374
+ <button class="time-button start-button" on:click={handleStartTime}
375
+ >Start</button
376
+ >
377
+ <button class="time-button end-button" on:click={handleEndTime}
378
+ >End</button
379
+ >
380
+ <button class="time-button now-button" on:click={handleNowTime}
381
+ >Now</button
382
+ >
383
+ <button class="time-button clear-button" on:click={handleClearValue}
384
+ >Select</button
385
+ >
386
+ </div>
267
387
  </div>
268
388
  {/if}
269
389
  </div>
@@ -283,7 +403,35 @@
283
403
  .time-section {
284
404
  display: flex;
285
405
  flex-direction: column;
286
- gap: 4px;
406
+ gap: 8px;
407
+ }
408
+
409
+ .time-buttons {
410
+ display: flex;
411
+ gap: 6px;
412
+ }
413
+
414
+ .time-button {
415
+ flex: 1;
416
+ padding: 6px 12px;
417
+ border: 1px solid var(--spectrum-global-color-gray-300);
418
+ border-radius: 4px;
419
+ background: var(--spectrum-global-color-gray-50);
420
+ color: var(--spectrum-global-color-gray-800);
421
+ font-size: 13px;
422
+ font-weight: 500;
423
+ cursor: pointer;
424
+ transition: all 0.15s ease;
425
+ }
426
+
427
+ .time-button:hover {
428
+ background: var(--spectrum-global-color-gray-100);
429
+ border-color: var(--spectrum-global-color-gray-400);
430
+ }
431
+
432
+ .time-button:active {
433
+ background: var(--spectrum-global-color-gray-200);
434
+ border-color: var(--spectrum-global-color-gray-500);
287
435
  }
288
436
 
289
437
  .time-label {