@sondor-technology/sondor-components 1.0.6 → 1.0.8

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 (65) hide show
  1. package/dist/components/calendar/calendar.d.ts +36 -0
  2. package/dist/components/calendar/calendar.d.ts.map +1 -0
  3. package/dist/components/calendar/calendar.js +439 -0
  4. package/dist/components/calendar/calendar.js.map +1 -0
  5. package/dist/components/cron-builder/cron-builder.d.ts +34 -0
  6. package/dist/components/cron-builder/cron-builder.d.ts.map +1 -0
  7. package/dist/components/cron-builder/cron-builder.js +456 -0
  8. package/dist/components/cron-builder/cron-builder.js.map +1 -0
  9. package/dist/components/form/form.js +2 -2
  10. package/dist/components/icon/icon-paths.d.ts +161 -86
  11. package/dist/components/icon/icon-paths.d.ts.map +1 -1
  12. package/dist/components/icon/icon-paths.js +175 -106
  13. package/dist/components/icon/icon-paths.js.map +1 -1
  14. package/dist/components/icon/icon.d.ts +1 -1
  15. package/dist/components/icon/icon.d.ts.map +1 -1
  16. package/dist/components/icon/icon.js +5 -5
  17. package/dist/components/icon/icon.js.map +1 -1
  18. package/dist/components/input/input.d.ts +1 -0
  19. package/dist/components/input/input.d.ts.map +1 -1
  20. package/dist/components/input/input.js +35 -12
  21. package/dist/components/input/input.js.map +1 -1
  22. package/dist/components/pin-input/pin-input.d.ts +33 -0
  23. package/dist/components/pin-input/pin-input.d.ts.map +1 -0
  24. package/dist/components/pin-input/pin-input.js +253 -0
  25. package/dist/components/pin-input/pin-input.js.map +1 -0
  26. package/dist/components/popover/popover.d.ts +26 -0
  27. package/dist/components/popover/popover.d.ts.map +1 -0
  28. package/dist/components/popover/popover.js +196 -0
  29. package/dist/components/popover/popover.js.map +1 -0
  30. package/dist/components/select/select.d.ts +4 -1
  31. package/dist/components/select/select.d.ts.map +1 -1
  32. package/dist/components/select/select.js +26 -4
  33. package/dist/components/select/select.js.map +1 -1
  34. package/dist/components/time-picker/time-picker.d.ts +38 -0
  35. package/dist/components/time-picker/time-picker.d.ts.map +1 -0
  36. package/dist/components/time-picker/time-picker.js +280 -0
  37. package/dist/components/time-picker/time-picker.js.map +1 -0
  38. package/dist/components/toggle-group/toggle-button.d.ts +23 -0
  39. package/dist/components/toggle-group/toggle-button.d.ts.map +1 -0
  40. package/dist/components/toggle-group/toggle-button.js +115 -0
  41. package/dist/components/toggle-group/toggle-button.js.map +1 -0
  42. package/dist/components/toggle-group/toggle-group.d.ts +32 -0
  43. package/dist/components/toggle-group/toggle-group.d.ts.map +1 -0
  44. package/dist/components/toggle-group/toggle-group.js +150 -0
  45. package/dist/components/toggle-group/toggle-group.js.map +1 -0
  46. package/dist/components/tooltip/tooltip.d.ts +33 -0
  47. package/dist/components/tooltip/tooltip.d.ts.map +1 -0
  48. package/dist/components/tooltip/tooltip.js +293 -0
  49. package/dist/components/tooltip/tooltip.js.map +1 -0
  50. package/dist/styles/index.css +1 -0
  51. package/package.json +29 -1
  52. package/src/components/calendar/calendar.ts +444 -0
  53. package/src/components/cron-builder/cron-builder.ts +452 -0
  54. package/src/components/form/form.ts +2 -2
  55. package/src/components/icon/icon-paths.ts +175 -106
  56. package/src/components/icon/icon.ts +6 -6
  57. package/src/components/input/input.ts +38 -14
  58. package/src/components/pin-input/pin-input.ts +249 -0
  59. package/src/components/popover/popover.ts +192 -0
  60. package/src/components/select/select.ts +25 -63
  61. package/src/components/time-picker/time-picker.ts +272 -0
  62. package/src/components/toggle-group/toggle-button.ts +104 -0
  63. package/src/components/toggle-group/toggle-group.ts +148 -0
  64. package/src/components/tooltip/tooltip.ts +286 -0
  65. package/src/styles/theme.css +2 -0
@@ -0,0 +1,444 @@
1
+ import {LitElement, html, css, PropertyValues} from "lit";
2
+ import {customElement, property, state} from "lit/decorators.js";
3
+ import {resetStyles} from "../../styles/reset.js";
4
+ import "../select/select.js"; // Ensure this path matches your project structure
5
+
6
+ const MONTH_NAMES = [
7
+ "January",
8
+ "February",
9
+ "March",
10
+ "April",
11
+ "May",
12
+ "June",
13
+ "July",
14
+ "August",
15
+ "September",
16
+ "October",
17
+ "November",
18
+ "December",
19
+ ];
20
+ const DAY_NAMES = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
21
+
22
+ const parseIsoLocal = (iso: string): Date | null => {
23
+ if (!iso) return null;
24
+ const [y, m, d] = iso.split(/\D/).map(Number);
25
+ if (!y || !m || !d) return null;
26
+ return new Date(y, m - 1, d);
27
+ };
28
+
29
+ const formatIsoLocal = (date: Date): string => {
30
+ const pad = (n: number) => n.toString().padStart(2, "0");
31
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
32
+ };
33
+
34
+ @customElement("st-calendar")
35
+ export class StCalendar extends LitElement {
36
+ static formAssociated = true;
37
+ private _internals: ElementInternals;
38
+
39
+ @property({type: String, reflect: true}) name = "";
40
+ @property({type: String}) value = "";
41
+ @property({type: String}) min = "";
42
+ @property({type: String}) max = "";
43
+
44
+ @property({type: Boolean, reflect: true}) disabled = false;
45
+ @property({type: Boolean, reflect: true}) required = false;
46
+ @property({type: Boolean, reflect: true}) invalid = false;
47
+
48
+ @state() private _viewMonth = new Date().getMonth();
49
+ @state() private _viewYear = new Date().getFullYear();
50
+
51
+ private _defaultValue = "";
52
+
53
+ constructor() {
54
+ super();
55
+ this._internals = this.attachInternals();
56
+ }
57
+
58
+ static styles = [
59
+ resetStyles,
60
+ css`
61
+ :host {
62
+ display: inline-block;
63
+ width: 320px;
64
+ background-color: var(--st-color-surface);
65
+ border: 1px solid var(--st-color-border);
66
+ border-radius: var(--st-radius-md);
67
+ padding: var(--st-spacing-md);
68
+ box-sizing: border-box;
69
+ }
70
+
71
+ :host([disabled]) {
72
+ opacity: 0.5;
73
+ pointer-events: none;
74
+ }
75
+
76
+ :host([invalid]) {
77
+ border-color: var(--st-color-error, #dc2626);
78
+ }
79
+
80
+ .header {
81
+ display: flex;
82
+ align-items: center;
83
+ justify-content: space-between;
84
+ margin-bottom: var(--st-spacing-md);
85
+ gap: var(--st-spacing-xs);
86
+ }
87
+
88
+ .header-controls {
89
+ display: flex;
90
+ gap: var(--st-spacing-xs);
91
+ flex: 1;
92
+ justify-content: center;
93
+ }
94
+
95
+ /* Shape the custom selects to fit side-by-side in the header */
96
+ .header-controls st-select:first-child {
97
+ width: 120px;
98
+ }
99
+
100
+ .header-controls st-select:last-child {
101
+ width: 90px;
102
+ }
103
+
104
+ .nav-button {
105
+ display: flex;
106
+ align-items: center;
107
+ justify-content: center;
108
+ width: 32px;
109
+ height: 42px; /* Matches the 42px min-height of your st-select combobox */
110
+ border: none;
111
+ background: transparent;
112
+ border-radius: var(--st-radius-sm);
113
+ color: var(--st-color-text-muted);
114
+ cursor: pointer;
115
+ transition: background-color 0.2s;
116
+ flex-shrink: 0;
117
+ }
118
+
119
+ .nav-button:hover {
120
+ background-color: var(--st-color-surface-hover, #f1f5f9);
121
+ color: var(--st-color-text);
122
+ }
123
+
124
+ .nav-button svg {
125
+ width: 20px;
126
+ height: 20px;
127
+ fill: none;
128
+ stroke: currentColor;
129
+ stroke-width: 2;
130
+ stroke-linecap: round;
131
+ stroke-linejoin: round;
132
+ }
133
+
134
+ .grid {
135
+ display: grid;
136
+ grid-template-columns: repeat(7, 1fr);
137
+ gap: 4px;
138
+ text-align: center;
139
+ }
140
+
141
+ .grid-row {
142
+ display: contents;
143
+ }
144
+
145
+ .day-name {
146
+ font-size: var(--st-font-size-xs);
147
+ font-weight: var(--st-font-weight-semibold);
148
+ color: var(--st-color-text-muted);
149
+ padding-bottom: var(--st-spacing-xs);
150
+ }
151
+
152
+ .day {
153
+ aspect-ratio: 1;
154
+ display: flex;
155
+ align-items: center;
156
+ justify-content: center;
157
+ border: none;
158
+ background: transparent;
159
+ border-radius: 50%;
160
+ color: var(--st-color-text);
161
+ font-size: var(--st-font-size-sm);
162
+ cursor: pointer;
163
+ transition: all 0.2s ease;
164
+ }
165
+
166
+ .day:hover:not(:disabled):not(.empty) {
167
+ background-color: var(--st-color-surface-hover, #f1f5f9);
168
+ }
169
+
170
+ .day:focus-visible {
171
+ outline: 2px solid var(--st-color-primary);
172
+ outline-offset: -2px;
173
+ }
174
+
175
+ .day:disabled {
176
+ color: var(--st-color-text-muted);
177
+ opacity: 0.3;
178
+ cursor: not-allowed;
179
+ }
180
+
181
+ .day.empty {
182
+ cursor: default;
183
+ pointer-events: none;
184
+ }
185
+
186
+ .day[aria-current="date"] {
187
+ font-weight: var(--st-font-weight-bold);
188
+ color: var(--st-color-primary);
189
+ background-color: var(--st-color-primary-light, #eff6ff);
190
+ }
191
+
192
+ .day[aria-selected="true"] {
193
+ background-color: var(--st-color-primary);
194
+ color: var(--st-color-on-primary, var(--st-color-bg, #ffffff));
195
+ font-weight: var(--st-font-weight-bold);
196
+ }
197
+
198
+ .day[aria-selected="true"]:hover {
199
+ background-color: var(--st-color-primary-dark, #1d4ed8);
200
+ }
201
+ `,
202
+ ];
203
+
204
+ override firstUpdated(changedProperties: PropertyValues) {
205
+ super.firstUpdated(changedProperties);
206
+ this._defaultValue = this.value;
207
+
208
+ if (this.value) {
209
+ const initDate = parseIsoLocal(this.value);
210
+ if (initDate) {
211
+ this._viewMonth = initDate.getMonth();
212
+ this._viewYear = initDate.getFullYear();
213
+ }
214
+ }
215
+ }
216
+
217
+ override updated(changedProperties: PropertyValues) {
218
+ super.updated(changedProperties);
219
+
220
+ if (changedProperties.has("value")) {
221
+ this._internals.setFormValue(this.value || null);
222
+ this._syncValidity();
223
+ }
224
+ }
225
+
226
+ private get _yearRange(): number[] {
227
+ const currentYear = new Date().getFullYear();
228
+ let minYear = currentYear - 50;
229
+ let maxYear = currentYear + 50;
230
+
231
+ if (this.min) {
232
+ const minDate = parseIsoLocal(this.min);
233
+ if (minDate) minYear = minDate.getFullYear();
234
+ }
235
+ if (this.max) {
236
+ const maxDate = parseIsoLocal(this.max);
237
+ if (maxDate) maxYear = maxDate.getFullYear();
238
+ }
239
+
240
+ const years = [];
241
+ for (let y = minYear; y <= maxYear; y++) {
242
+ years.push(y);
243
+ }
244
+ return years;
245
+ }
246
+
247
+ private _syncValidity() {
248
+ this.invalid = false;
249
+
250
+ if (this.required && !this.value) {
251
+ this._internals.setValidity({valueMissing: true}, "Please select a date.");
252
+ this.invalid = true;
253
+ return;
254
+ }
255
+
256
+ if (this.value) {
257
+ const minDate = parseIsoLocal(this.min);
258
+ const maxDate = parseIsoLocal(this.max);
259
+ const valDate = parseIsoLocal(this.value);
260
+
261
+ if (valDate && minDate && valDate < minDate) {
262
+ this._internals.setValidity({rangeUnderflow: true}, `Date must be after ${this.min}.`);
263
+ this.invalid = true;
264
+ return;
265
+ }
266
+ if (valDate && maxDate && valDate > maxDate) {
267
+ this._internals.setValidity({rangeOverflow: true}, `Date must be before ${this.max}.`);
268
+ this.invalid = true;
269
+ return;
270
+ }
271
+ }
272
+
273
+ this._internals.setValidity({});
274
+ }
275
+
276
+ formResetCallback() {
277
+ this.value = this._defaultValue;
278
+ if (this.value) {
279
+ const initDate = parseIsoLocal(this.value);
280
+ if (initDate) {
281
+ this._viewMonth = initDate.getMonth();
282
+ this._viewYear = initDate.getFullYear();
283
+ }
284
+ }
285
+ }
286
+
287
+ public checkValidity() {
288
+ return this._internals.checkValidity();
289
+ }
290
+ public reportValidity() {
291
+ return this._internals.reportValidity();
292
+ }
293
+
294
+ private _prevMonth() {
295
+ if (this._viewMonth === 0) {
296
+ this._viewMonth = 11;
297
+ this._viewYear--;
298
+ } else {
299
+ this._viewMonth--;
300
+ }
301
+ }
302
+
303
+ private _nextMonth() {
304
+ if (this._viewMonth === 11) {
305
+ this._viewMonth = 0;
306
+ this._viewYear++;
307
+ } else {
308
+ this._viewMonth++;
309
+ }
310
+ }
311
+
312
+ private _selectDate(day: number) {
313
+ const selected = new Date(this._viewYear, this._viewMonth, day);
314
+ this.value = formatIsoLocal(selected);
315
+
316
+ this.dispatchEvent(new Event("change", {bubbles: true, composed: true}));
317
+ this.dispatchEvent(new Event("input", {bubbles: true, composed: true}));
318
+ }
319
+
320
+ private _isDateDisabled(year: number, month: number, day: number): boolean {
321
+ if (this.disabled) return true;
322
+
323
+ const date = new Date(year, month, day);
324
+ const minDate = parseIsoLocal(this.min);
325
+ const maxDate = parseIsoLocal(this.max);
326
+
327
+ if (minDate && date < minDate) return true;
328
+ if (maxDate && date > maxDate) return true;
329
+
330
+ return false;
331
+ }
332
+
333
+ render() {
334
+ const today = new Date();
335
+ const isToday = (day: number) =>
336
+ today.getDate() === day &&
337
+ today.getMonth() === this._viewMonth &&
338
+ today.getFullYear() === this._viewYear;
339
+
340
+ const selectedDate = parseIsoLocal(this.value);
341
+ const isSelected = (day: number) =>
342
+ selectedDate !== null &&
343
+ selectedDate.getDate() === day &&
344
+ selectedDate.getMonth() === this._viewMonth &&
345
+ selectedDate.getFullYear() === this._viewYear;
346
+
347
+ const firstDayOfMonth = new Date(this._viewYear, this._viewMonth, 1).getDay();
348
+ const daysInMonth = new Date(this._viewYear, this._viewMonth + 1, 0).getDate();
349
+
350
+ const blanks = Array.from({length: firstDayOfMonth}, (_, i) => i);
351
+ const days = Array.from({length: daysInMonth}, (_, i) => i + 1);
352
+
353
+ const allCells = [
354
+ ...blanks.map(() => html`<div class="day empty" role="gridcell"></div>`),
355
+ ...days.map((day) => {
356
+ const disabled = this._isDateDisabled(this._viewYear, this._viewMonth, day);
357
+ return html`
358
+ <button
359
+ class="day"
360
+ role="gridcell"
361
+ type="button"
362
+ ?disabled=${disabled}
363
+ aria-label=${`${MONTH_NAMES[this._viewMonth]} ${day}, ${this._viewYear}`}
364
+ aria-current=${isToday(day) ? "date" : "false"}
365
+ aria-selected=${isSelected(day) ? "true" : "false"}
366
+ @click=${() => this._selectDate(day)}
367
+ >
368
+ ${day}
369
+ </button>
370
+ `;
371
+ }),
372
+ ];
373
+
374
+ const weeks = [];
375
+ for (let i = 0; i < allCells.length; i += 7) {
376
+ weeks.push(allCells.slice(i, i + 7));
377
+ }
378
+
379
+ // 🚀 FIX: Generate the option array objects as required by your st-select API
380
+ const monthOptions = MONTH_NAMES.map((name, i) => ({label: name, value: String(i)}));
381
+ const yearOptions = this._yearRange.map((year) => ({label: String(year), value: String(year)}));
382
+
383
+ return html`
384
+ <div class="header">
385
+ <button
386
+ class="nav-button"
387
+ type="button"
388
+ aria-label="Previous Month"
389
+ @click=${this._prevMonth}
390
+ ?disabled=${this.disabled}
391
+ >
392
+ <svg viewBox="0 0 24 24"><path d="M15 18l-6-6 6-6"></path></svg>
393
+ </button>
394
+
395
+ <div class="header-controls">
396
+ <st-select
397
+ label="Month"
398
+ hide-label
399
+ .options=${monthOptions}
400
+ .value=${String(this._viewMonth)}
401
+ ?disabled=${this.disabled}
402
+ @change=${(e: any) => (this._viewMonth = Number(e.target.value))}
403
+ ></st-select>
404
+
405
+ <st-select
406
+ label="Year"
407
+ hide-label
408
+ .options=${yearOptions}
409
+ .value=${String(this._viewYear)}
410
+ ?disabled=${this.disabled}
411
+ @change=${(e: any) => (this._viewYear = Number(e.target.value))}
412
+ ></st-select>
413
+ </div>
414
+
415
+ <button
416
+ class="nav-button"
417
+ type="button"
418
+ aria-label="Next Month"
419
+ @click=${this._nextMonth}
420
+ ?disabled=${this.disabled}
421
+ >
422
+ <svg viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"></path></svg>
423
+ </button>
424
+ </div>
425
+
426
+ <div class="grid" role="grid" aria-label="Calendar">
427
+ <div class="grid-row" role="row">
428
+ ${DAY_NAMES.map(
429
+ (name) => html`
430
+ <div class="day-name" role="columnheader" aria-label=${name}>${name}</div>
431
+ `,
432
+ )}
433
+ </div>
434
+ ${weeks.map((week) => html` <div class="grid-row" role="row">${week}</div> `)}
435
+ </div>
436
+ `;
437
+ }
438
+ }
439
+
440
+ declare global {
441
+ interface HTMLElementTagNameMap {
442
+ "st-calendar": StCalendar;
443
+ }
444
+ }