@vobs/ui 0.2.0 → 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/LICENSE +1 -1
- package/README.md +60 -0
- package/package.json +52 -102
- package/src/activity-rail.ts +125 -0
- package/src/alert.ts +65 -0
- package/src/assets/icons/alert-circle.svg +1 -0
- package/src/assets/icons/alert-triangle.svg +1 -0
- package/src/assets/icons/alert.d27e24.svg +1 -0
- package/src/assets/icons/alert.svg +1 -0
- package/src/assets/icons/apple.svg +1 -0
- package/src/assets/icons/arrow-down.svg +1 -0
- package/src/assets/icons/arrow-expand.svg +1 -0
- package/src/assets/icons/arrow-left.svg +1 -0
- package/src/assets/icons/arrow-minimize.svg +1 -0
- package/src/assets/icons/arrow-right-to-line.svg +1 -0
- package/src/assets/icons/arrow-right.svg +1 -0
- package/src/assets/icons/arrow-up-right.svg +1 -0
- package/src/assets/icons/arrow-up.0c0c0d.svg +1 -0
- package/src/assets/icons/arrow-up.svg +1 -0
- package/src/assets/icons/at.svg +1 -0
- package/src/assets/icons/atom.0c0c0d.svg +1 -0
- package/src/assets/icons/atom.svg +1 -0
- package/src/assets/icons/bar.svg +1 -0
- package/src/assets/icons/bell.svg +1 -0
- package/src/assets/icons/box.svg +1 -0
- package/src/assets/icons/bug.svg +1 -0
- package/src/assets/icons/calendar.svg +1 -0
- package/src/assets/icons/check-circle.33c192.svg +1 -0
- package/src/assets/icons/check-circle.svg +1 -0
- package/src/assets/icons/check.d1d3db.svg +1 -0
- package/src/assets/icons/check.svg +1 -0
- package/src/assets/icons/chevron-down.svg +1 -0
- package/src/assets/icons/chevron-right.svg +1 -0
- package/src/assets/icons/chevron-up.svg +1 -0
- package/src/assets/icons/circle.svg +1 -0
- package/src/assets/icons/clock.svg +1 -0
- package/src/assets/icons/cmd.svg +1 -0
- package/src/assets/icons/code.svg +1 -0
- package/src/assets/icons/columns.svg +1 -0
- package/src/assets/icons/copy.svg +1 -0
- package/src/assets/icons/cpu.svg +1 -0
- package/src/assets/icons/dashboard.0c0c0d.svg +1 -0
- package/src/assets/icons/dashboard.svg +1 -0
- package/src/assets/icons/dollar.svg +1 -0
- package/src/assets/icons/download.svg +1 -0
- package/src/assets/icons/edit.svg +1 -0
- package/src/assets/icons/extensions.svg +1 -0
- package/src/assets/icons/external.svg +1 -0
- package/src/assets/icons/eye.svg +1 -0
- package/src/assets/icons/file-text.svg +1 -0
- package/src/assets/icons/file.svg +1 -0
- package/src/assets/icons/files.svg +1 -0
- package/src/assets/icons/filter.svg +1 -0
- package/src/assets/icons/folder.svg +1 -0
- package/src/assets/icons/gear.svg +1 -0
- package/src/assets/icons/git.svg +1 -0
- package/src/assets/icons/github.svg +1 -0
- package/src/assets/icons/globe.svg +1 -0
- package/src/assets/icons/grid-2x2.svg +1 -0
- package/src/assets/icons/hash.svg +1 -0
- package/src/assets/icons/heart.svg +1 -0
- package/src/assets/icons/help.svg +1 -0
- package/src/assets/icons/home.svg +1 -0
- package/src/assets/icons/image.svg +1 -0
- package/src/assets/icons/info-circle.svg +1 -0
- package/src/assets/icons/info-square.svg +1 -0
- package/src/assets/icons/info.387bff.svg +1 -0
- package/src/assets/icons/info.svg +1 -0
- package/src/assets/icons/key.svg +1 -0
- package/src/assets/icons/layers.svg +1 -0
- package/src/assets/icons/layout.svg +1 -0
- package/src/assets/icons/link.svg +1 -0
- package/src/assets/icons/list.svg +1 -0
- package/src/assets/icons/lock.svg +1 -0
- package/src/assets/icons/log-out.svg +1 -0
- package/src/assets/icons/logo.svg +1 -0
- package/src/assets/icons/mail.svg +1 -0
- package/src/assets/icons/menu.svg +1 -0
- package/src/assets/icons/message-circle.svg +1 -0
- package/src/assets/icons/message-plus.svg +1 -0
- package/src/assets/icons/message-square.svg +1 -0
- package/src/assets/icons/mic.svg +1 -0
- package/src/assets/icons/minus.svg +1 -0
- package/src/assets/icons/moon.svg +1 -0
- package/src/assets/icons/more-h.svg +1 -0
- package/src/assets/icons/mouse.svg +1 -0
- package/src/assets/icons/palette.svg +1 -0
- package/src/assets/icons/panel-bottom.svg +1 -0
- package/src/assets/icons/pause.svg +1 -0
- package/src/assets/icons/play.svg +1 -0
- package/src/assets/icons/plug.svg +1 -0
- package/src/assets/icons/plus.0c0c0d.svg +1 -0
- package/src/assets/icons/plus.svg +1 -0
- package/src/assets/icons/refresh.svg +1 -0
- package/src/assets/icons/ruler.svg +1 -0
- package/src/assets/icons/scroll-text.svg +1 -0
- package/src/assets/icons/search-menu.svg +1 -0
- package/src/assets/icons/search.svg +1 -0
- package/src/assets/icons/send.0c0c0d.svg +1 -0
- package/src/assets/icons/send.svg +1 -0
- package/src/assets/icons/settings.0c0c0d.svg +1 -0
- package/src/assets/icons/settings.svg +1 -0
- package/src/assets/icons/shield.svg +1 -0
- package/src/assets/icons/sidebar-left.svg +1 -0
- package/src/assets/icons/sidebar-right.svg +1 -0
- package/src/assets/icons/sliders.svg +1 -0
- package/src/assets/icons/sparkles.32f08c.svg +1 -0
- package/src/assets/icons/sparkles.svg +1 -0
- package/src/assets/icons/square.svg +1 -0
- package/src/assets/icons/star.svg +1 -0
- package/src/assets/icons/sun.svg +1 -0
- package/src/assets/icons/terminal.svg +1 -0
- package/src/assets/icons/trash.svg +1 -0
- package/src/assets/icons/trending-down.svg +1 -0
- package/src/assets/icons/trending-up.svg +1 -0
- package/src/assets/icons/type.svg +1 -0
- package/src/assets/icons/upload.svg +1 -0
- package/src/assets/icons/user-circle.svg +1 -0
- package/src/assets/icons/user.svg +1 -0
- package/src/assets/icons/users.svg +1 -0
- package/src/assets/icons/wrench.svg +1 -0
- package/src/assets/icons/x-circle.svg +1 -0
- package/src/assets/icons/x.svg +1 -0
- package/src/assets/icons/zap.0c0c0d.svg +1 -0
- package/src/assets/icons/zap.svg +1 -0
- package/src/avatar.ts +64 -0
- package/src/button.ts +88 -0
- package/src/card.ts +59 -0
- package/src/chat-composer.ts +156 -0
- package/src/dialog.ts +127 -0
- package/src/drawer.ts +131 -0
- package/src/editor-tabs.ts +184 -0
- package/src/file-tree.ts +214 -0
- package/src/forms.ts +270 -0
- package/src/icon.ts +117 -0
- package/src/icons.js +165 -0
- package/src/index.test.ts +213 -0
- package/src/index.ts +94 -0
- package/src/kbd.ts +72 -0
- package/src/menu.ts +119 -0
- package/src/nav-list.ts +157 -0
- package/src/overlay.test.ts +66 -0
- package/src/overlay.ts +205 -0
- package/src/page-header.ts +97 -0
- package/src/pagination.ts +163 -0
- package/src/second-batch.test.ts +205 -0
- package/src/stat-card.ts +92 -0
- package/src/status-bar.ts +108 -0
- package/src/styles/base.css +240 -0
- package/src/styles/components.css +1234 -0
- package/{dist → src/styles}/styles.css +2 -0
- package/src/styles/toast.css +57 -0
- package/src/styles/tokens.css +438 -0
- package/src/styles/vui.css +47 -0
- package/src/table.ts +191 -0
- package/src/tabs.ts +100 -0
- package/src/tag.ts +33 -0
- package/src/third-batch.test.ts +137 -0
- package/src/toast.test.ts +55 -0
- package/src/toast.ts +79 -0
- package/src/types.ts +25 -0
- package/src/utils.ts +221 -0
- package/src/workbench-titlebar.ts +137 -0
- package/dist/app-shell-dom.d.ts +0 -24
- package/dist/app-shell-dom.js +0 -573
- package/dist/app-shell.d.ts +0 -199
- package/dist/app-shell.js +0 -370
- package/dist/base.css +0 -52
- package/dist/calendar-picker.d.ts +0 -45
- package/dist/calendar-picker.js +0 -217
- package/dist/calendar.d.ts +0 -95
- package/dist/calendar.js +0 -194
- package/dist/complex-inputs.d.ts +0 -300
- package/dist/complex-inputs.js +0 -862
- package/dist/components.css +0 -4568
- package/dist/data-display.d.ts +0 -1063
- package/dist/data-display.js +0 -2298
- package/dist/feedback.d.ts +0 -88
- package/dist/feedback.js +0 -281
- package/dist/forms.d.ts +0 -378
- package/dist/forms.js +0 -1015
- package/dist/icons-config.d.ts +0 -25
- package/dist/icons-config.js +0 -14
- package/dist/icons.d.ts +0 -41
- package/dist/icons.js +0 -185
- package/dist/index.d.ts +0 -27
- package/dist/index.js +0 -34
- package/dist/locale/en-US.d.ts +0 -31
- package/dist/locale/en-US.js +0 -20
- package/dist/locale/zh-CN.d.ts +0 -31
- package/dist/locale/zh-CN.js +0 -20
- package/dist/navigation.d.ts +0 -609
- package/dist/navigation.js +0 -1707
- package/dist/overlay.d.ts +0 -304
- package/dist/overlay.js +0 -969
- package/dist/primitives.d.ts +0 -268
- package/dist/primitives.js +0 -764
- package/dist/theme-data.d.ts +0 -363
- package/dist/theme-data.js +0 -374
- package/dist/theme.d.ts +0 -79
- package/dist/theme.js +0 -262
- package/dist/tokens.css +0 -449
- package/dist/tree.d.ts +0 -144
- package/dist/tree.js +0 -469
- package/dist/virtual-list.d.ts +0 -190
- package/dist/virtual-list.js +0 -521
- package/dist/workbench.d.ts +0 -136
- package/dist/workbench.js +0 -156
package/dist/calendar-picker.js
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
/** @license MIT
|
|
2
|
-
* Copyright (c) 2026 vobsjs
|
|
3
|
-
* @vobs/ui
|
|
4
|
-
*/
|
|
5
|
-
import { effect, signal } from '@vobs/reactivity';
|
|
6
|
-
import { createCompiledTemplate, defineComponent, } from '@vobs/runtime-dom';
|
|
7
|
-
import { createServerTemplate, escapeHtmlAttribute } from '@vobs/server-renderer';
|
|
8
|
-
import { createCalendarPlans } from './calendar.js';
|
|
9
|
-
const ROOT_CLASS = 'kui-calendar-picker';
|
|
10
|
-
/** Initial displayed month: month prop → value's month → current month. */
|
|
11
|
-
function resolveInitialMonth(month, value) {
|
|
12
|
-
if (month !== undefined && /^\d{4}-\d{2}$/.test(month))
|
|
13
|
-
return month;
|
|
14
|
-
const fromValue = value === undefined ? undefined : value.slice(0, 7);
|
|
15
|
-
if (fromValue !== undefined && /^\d{4}-\d{2}$/.test(fromValue))
|
|
16
|
-
return fromValue;
|
|
17
|
-
const now = new Date();
|
|
18
|
-
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
|
|
19
|
-
}
|
|
20
|
-
/** Shifts the displayed month (rolls over across years). */
|
|
21
|
-
function shiftMonth(current, delta) {
|
|
22
|
-
const parts = current.split('-');
|
|
23
|
-
const year = Number(parts[0]);
|
|
24
|
-
const month = Number(parts[1]);
|
|
25
|
-
if (!Number.isFinite(year) || !Number.isFinite(month))
|
|
26
|
-
return current;
|
|
27
|
-
const shifted = new Date(year, month - 1 + delta, 1);
|
|
28
|
-
return `${shifted.getFullYear()}-${String(shifted.getMonth() + 1).padStart(2, '0')}`;
|
|
29
|
-
}
|
|
30
|
-
/** Renders the popover content: header (prev/title/next) + weekday row + 42 cells. Events are delegated, so no rebinding is needed. */
|
|
31
|
-
function renderPopover(popover, scope, displayMonth) {
|
|
32
|
-
popover.replaceChildren();
|
|
33
|
-
const plans = createCalendarPlans({
|
|
34
|
-
id: 'kui-calendar',
|
|
35
|
-
...(scope.props.value?.value === undefined ? {} : { value: scope.props.value.value }),
|
|
36
|
-
month: displayMonth,
|
|
37
|
-
...(scope.props.min?.value === undefined ? {} : { min: scope.props.min.value }),
|
|
38
|
-
...(scope.props.max?.value === undefined ? {} : { max: scope.props.max.value }),
|
|
39
|
-
...(scope.props.disabled?.value === undefined ? {} : { disabled: scope.props.disabled.value }),
|
|
40
|
-
locale: scope.props.locale?.value ?? 'zh-CN',
|
|
41
|
-
});
|
|
42
|
-
const header = document.createElement('div');
|
|
43
|
-
header.className = plans.header.className;
|
|
44
|
-
const prev = document.createElement('button');
|
|
45
|
-
prev.className = plans.prevButton.className;
|
|
46
|
-
prev.type = 'button';
|
|
47
|
-
prev.setAttribute('data-direction', 'prev');
|
|
48
|
-
prev.setAttribute('aria-label', 'Previous month');
|
|
49
|
-
const title = document.createElement('span');
|
|
50
|
-
title.className = plans.title.className;
|
|
51
|
-
title.textContent = String(plans.title.attrs['data-label'] ?? '');
|
|
52
|
-
const next = document.createElement('button');
|
|
53
|
-
next.className = plans.nextButton.className;
|
|
54
|
-
next.type = 'button';
|
|
55
|
-
next.setAttribute('data-direction', 'next');
|
|
56
|
-
next.setAttribute('aria-label', 'Next month');
|
|
57
|
-
header.append(prev, title, next);
|
|
58
|
-
const weekdayRow = document.createElement('div');
|
|
59
|
-
weekdayRow.className = plans.weekdayRow.className;
|
|
60
|
-
for (const weekday of plans.weekdays) {
|
|
61
|
-
const span = document.createElement('span');
|
|
62
|
-
span.className = weekday.className;
|
|
63
|
-
span.textContent = String(weekday.attrs['data-label'] ?? '');
|
|
64
|
-
weekdayRow.append(span);
|
|
65
|
-
}
|
|
66
|
-
const grid = document.createElement('div');
|
|
67
|
-
grid.className = plans.grid.className;
|
|
68
|
-
grid.setAttribute('role', 'grid');
|
|
69
|
-
for (const day of plans.days) {
|
|
70
|
-
const button = document.createElement('button');
|
|
71
|
-
button.className = day.className;
|
|
72
|
-
button.type = 'button';
|
|
73
|
-
for (const [name, value] of Object.entries(day.attrs)) {
|
|
74
|
-
if (value === undefined)
|
|
75
|
-
continue;
|
|
76
|
-
button.setAttribute(name, typeof value === 'boolean' ? String(value) : String(value));
|
|
77
|
-
}
|
|
78
|
-
button.textContent = String(day.day);
|
|
79
|
-
grid.append(button);
|
|
80
|
-
}
|
|
81
|
-
popover.append(header, weekdayRow, grid);
|
|
82
|
-
}
|
|
83
|
-
/** Shared picker assembly for mount/hydrate (input + popover state machine + event delegation + outside-click/Escape close + dispose). */
|
|
84
|
-
function mountCalendarPicker(owner, root, scope, _environment) {
|
|
85
|
-
const input = document.createElement('input');
|
|
86
|
-
input.className = 'kui-calendar-picker__input';
|
|
87
|
-
input.type = 'text';
|
|
88
|
-
input.readOnly = true;
|
|
89
|
-
input.setAttribute('role', 'combobox');
|
|
90
|
-
input.setAttribute('aria-haspopup', 'dialog');
|
|
91
|
-
input.setAttribute('aria-expanded', 'false');
|
|
92
|
-
const popover = document.createElement('div');
|
|
93
|
-
popover.className = 'kui-calendar-popover';
|
|
94
|
-
popover.setAttribute('role', 'dialog');
|
|
95
|
-
popover.hidden = true;
|
|
96
|
-
root.append(input, popover);
|
|
97
|
-
const open = signal(false);
|
|
98
|
-
const displayMonth = signal(resolveInitialMonth(scope.props.month?.value, scope.props.value?.value));
|
|
99
|
-
const stopValueSync = effect(() => {
|
|
100
|
-
const value = scope.props.value?.value ?? '';
|
|
101
|
-
if (input.value !== value)
|
|
102
|
-
input.value = value;
|
|
103
|
-
});
|
|
104
|
-
const stopPlaceholder = effect(() => {
|
|
105
|
-
input.placeholder = scope.props.placeholder?.value ?? '';
|
|
106
|
-
});
|
|
107
|
-
const stopRender = effect(() => {
|
|
108
|
-
const isOpen = open.value;
|
|
109
|
-
const month = displayMonth.value;
|
|
110
|
-
void scope.props.value?.value;
|
|
111
|
-
void scope.props.locale?.value;
|
|
112
|
-
if (isOpen) {
|
|
113
|
-
renderPopover(popover, scope, month);
|
|
114
|
-
}
|
|
115
|
-
popover.hidden = !isOpen;
|
|
116
|
-
input.setAttribute('aria-expanded', String(isOpen));
|
|
117
|
-
});
|
|
118
|
-
const stopDisabled = effect(() => {
|
|
119
|
-
input.disabled = scope.props.disabled?.value === true;
|
|
120
|
-
});
|
|
121
|
-
const onPopoverClick = (event) => {
|
|
122
|
-
const target = event.target;
|
|
123
|
-
if (target === null)
|
|
124
|
-
return;
|
|
125
|
-
const isoDay = target.closest('[data-iso]');
|
|
126
|
-
if (isoDay !== null) {
|
|
127
|
-
const iso = isoDay.dataset['iso'];
|
|
128
|
-
if (iso !== undefined && scope.props.disabled?.value !== true) {
|
|
129
|
-
scope.emit('update', iso);
|
|
130
|
-
open.value = false;
|
|
131
|
-
scope.emit('close');
|
|
132
|
-
}
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
const nav = target.closest('[data-direction]');
|
|
136
|
-
if (nav !== null) {
|
|
137
|
-
const direction = nav.dataset['direction'];
|
|
138
|
-
if (direction === 'prev' || direction === 'next') {
|
|
139
|
-
displayMonth.value = shiftMonth(displayMonth.value, direction === 'prev' ? -1 : 1);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
popover.addEventListener('click', onPopoverClick);
|
|
144
|
-
const onInputClick = () => {
|
|
145
|
-
if (scope.props.disabled?.value === true)
|
|
146
|
-
return;
|
|
147
|
-
const next = !open.value;
|
|
148
|
-
open.value = next;
|
|
149
|
-
scope.emit(next ? 'open' : 'close');
|
|
150
|
-
};
|
|
151
|
-
input.addEventListener('click', onInputClick);
|
|
152
|
-
const onDocumentClick = (event) => {
|
|
153
|
-
if (!open.value)
|
|
154
|
-
return;
|
|
155
|
-
const target = event.target;
|
|
156
|
-
if (target !== null && root.contains(target))
|
|
157
|
-
return;
|
|
158
|
-
open.value = false;
|
|
159
|
-
scope.emit('close');
|
|
160
|
-
};
|
|
161
|
-
document.addEventListener('mousedown', onDocumentClick);
|
|
162
|
-
const onKeyDown = (event) => {
|
|
163
|
-
if (event.key !== 'Escape' || !open.value)
|
|
164
|
-
return;
|
|
165
|
-
open.value = false;
|
|
166
|
-
scope.emit('close');
|
|
167
|
-
};
|
|
168
|
-
document.addEventListener('keydown', onKeyDown);
|
|
169
|
-
owner.own(() => {
|
|
170
|
-
stopValueSync();
|
|
171
|
-
stopPlaceholder();
|
|
172
|
-
stopRender();
|
|
173
|
-
stopDisabled();
|
|
174
|
-
popover.removeEventListener('click', onPopoverClick);
|
|
175
|
-
input.removeEventListener('click', onInputClick);
|
|
176
|
-
document.removeEventListener('mousedown', onDocumentClick);
|
|
177
|
-
document.removeEventListener('keydown', onKeyDown);
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
const template = createCompiledTemplate({
|
|
181
|
-
templateId: 'k-calendar-picker',
|
|
182
|
-
sourceId: '@vobs/ui/calendar-picker',
|
|
183
|
-
rootTag: 'div',
|
|
184
|
-
mount(owner, scope, environment) {
|
|
185
|
-
const root = document.createElement('div');
|
|
186
|
-
root.className = ROOT_CLASS;
|
|
187
|
-
mountCalendarPicker(owner, root, scope, environment);
|
|
188
|
-
return { root, refs: {} };
|
|
189
|
-
},
|
|
190
|
-
hydrate(owner, root, scope, environment) {
|
|
191
|
-
if (root.localName !== 'div')
|
|
192
|
-
return undefined;
|
|
193
|
-
root.classList.add(ROOT_CLASS);
|
|
194
|
-
mountCalendarPicker(owner, root, scope, environment);
|
|
195
|
-
return { root, refs: {} };
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
/** Component definition: `<k-calendar-picker :value="date" @update="onDate" />`. */
|
|
199
|
-
export const CalendarPicker = defineComponent({
|
|
200
|
-
template,
|
|
201
|
-
props: ['value', 'month', 'min', 'max', 'disabled', 'locale', 'placeholder', 'className'],
|
|
202
|
-
emits: ['update', 'open', 'close'],
|
|
203
|
-
setup(context) {
|
|
204
|
-
return { props: context.props, emit: context.emit.bind(context) };
|
|
205
|
-
},
|
|
206
|
-
});
|
|
207
|
-
/** Server template (SSR): only the input (popover closed by default). */
|
|
208
|
-
export const CalendarPickerServerTemplate = createServerTemplate({
|
|
209
|
-
templateId: 'k-calendar-picker',
|
|
210
|
-
sourceId: '@vobs/ui/calendar-picker',
|
|
211
|
-
rootTag: 'div',
|
|
212
|
-
render(scope) {
|
|
213
|
-
const value = scope.props.value?.value ?? '';
|
|
214
|
-
const placeholder = scope.props.placeholder?.value ?? '';
|
|
215
|
-
return `<div class="${ROOT_CLASS}"><input class="kui-calendar-picker__input" type="text" readonly value="${escapeHtmlAttribute(value)}" placeholder="${escapeHtmlAttribute(placeholder)}" /></div>`;
|
|
216
|
-
},
|
|
217
|
-
});
|
package/dist/calendar.d.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/** @license MIT
|
|
2
|
-
* Copyright (c) 2026 vobsjs
|
|
3
|
-
* @vobs/ui
|
|
4
|
-
*/
|
|
5
|
-
import { type UiAttributes, type UiDomPlan } from './primitives.js';
|
|
6
|
-
/**
|
|
7
|
-
* Calendar panel plans (official component, scheme A — implemented inside the ui library, not an adapter).
|
|
8
|
-
*
|
|
9
|
-
* Month-view grid: header (prev/title/next) + weekday row + 42-cell date grid (6 weeks, including leading/trailing out-of-month cells).
|
|
10
|
-
* Homogeneous with other plans components in the ui package (combobox/cascader pattern): static DOM plans + derived state.
|
|
11
|
-
* Day cells are dynamic data but expressed as a static plans array (days: readonly CalendarDayPlan[]) — apps render them via templates.
|
|
12
|
-
*
|
|
13
|
-
* Localization: Intl API (weekday/month names follow locale, default zh-CN; no @vobs/i18n dependency).
|
|
14
|
-
* Date semantics: ISO 'YYYY-MM-DD' strings (lexicographic order = chronological order, min/max compared as strings).
|
|
15
|
-
* State derivation: disabled > selected > today > outside > idle (data-state applied by createUiDomPlan).
|
|
16
|
-
*/
|
|
17
|
-
export type CalendarDayState = 'idle' | 'today' | 'selected' | 'outside' | 'disabled';
|
|
18
|
-
export interface CalendarDayPlan extends UiDomPlan<'button'> {
|
|
19
|
-
/** Day of month (1-31). */
|
|
20
|
-
readonly day: number;
|
|
21
|
-
/** ISO date 'YYYY-MM-DD'. */
|
|
22
|
-
readonly iso: string;
|
|
23
|
-
/** Whether the day belongs to the displayed month (false = leading/trailing out-of-month cell). */
|
|
24
|
-
readonly inMonth: boolean;
|
|
25
|
-
/** Derived state (disabled > selected > today > outside > idle). */
|
|
26
|
-
readonly dayState: CalendarDayState;
|
|
27
|
-
}
|
|
28
|
-
export interface CalendarPlanOptions {
|
|
29
|
-
/** Root element id (child element ids are derived from it). */
|
|
30
|
-
readonly id: string;
|
|
31
|
-
/** Selected value 'YYYY-MM-DD' (renders the selected state). */
|
|
32
|
-
readonly value?: string;
|
|
33
|
-
/** Displayed month 'YYYY-MM' (defaults to the current month). */
|
|
34
|
-
readonly month?: string;
|
|
35
|
-
/** Minimum selectable date 'YYYY-MM-DD' (inclusive). */
|
|
36
|
-
readonly min?: string;
|
|
37
|
-
/** Maximum selectable date 'YYYY-MM-DD' (inclusive). */
|
|
38
|
-
readonly max?: string;
|
|
39
|
-
/** Disables the whole calendar. */
|
|
40
|
-
readonly disabled?: boolean;
|
|
41
|
-
/** BCP 47 locale (default 'zh-CN'); determines weekday/month names and title format. */
|
|
42
|
-
readonly locale?: string;
|
|
43
|
-
readonly className?: string;
|
|
44
|
-
readonly attrs?: UiAttributes;
|
|
45
|
-
}
|
|
46
|
-
export interface CalendarPlans {
|
|
47
|
-
readonly root: UiDomPlan<'div'>;
|
|
48
|
-
readonly header: UiDomPlan<'div'>;
|
|
49
|
-
readonly prevButton: UiDomPlan<'button'>;
|
|
50
|
-
readonly nextButton: UiDomPlan<'button'>;
|
|
51
|
-
readonly title: UiDomPlan<'span'>;
|
|
52
|
-
readonly weekdayRow: UiDomPlan<'div'>;
|
|
53
|
-
/** 7 weekday abbreviations (Sunday-first, follows locale). */
|
|
54
|
-
readonly weekdays: readonly UiDomPlan<'span'>[];
|
|
55
|
-
readonly grid: UiDomPlan<'div'>;
|
|
56
|
-
/** 42 cells (6 weeks, starting from the Sunday of the displayed month's first day's week). */
|
|
57
|
-
readonly days: readonly CalendarDayPlan[];
|
|
58
|
-
}
|
|
59
|
-
/** Date → ISO 'YYYY-MM-DD' (local timezone). */
|
|
60
|
-
export declare function formatCalendarDate(date: Date): string;
|
|
61
|
-
/** ISO 'YYYY-MM-DD' → local Date (returns undefined for invalid/overflow input). */
|
|
62
|
-
export declare function parseCalendarDate(iso: string): Date | undefined;
|
|
63
|
-
/**
|
|
64
|
-
* Builds calendar panel plans (42-cell month view).
|
|
65
|
-
*
|
|
66
|
-
* Layout convention: Sunday-first (Chinese convention) — the grid starts on the Sunday of the week
|
|
67
|
-
* containing the displayed month's first day, for 42 cells total.
|
|
68
|
-
* Per-cell state derivation: disabled (out of min/max or whole calendar disabled) > selected > today > outside > idle.
|
|
69
|
-
*/
|
|
70
|
-
export declare function createCalendarPlans(options: CalendarPlanOptions): CalendarPlans;
|
|
71
|
-
/**
|
|
72
|
-
* Date Range Picker plans — two range inputs (start/end) + a dual-calendar popover container.
|
|
73
|
-
* Spec source: common enterprise-backend component set (high-frequency list filtering; no UIStyle spec, this is an out-of-spec component).
|
|
74
|
-
* Calendar content is rendered by the app into startCalendar / endCalendar using createCalendarPlans.
|
|
75
|
-
*/
|
|
76
|
-
export interface DateRangePickerPlans {
|
|
77
|
-
readonly root: UiDomPlan<'div'>;
|
|
78
|
-
readonly startInput: UiDomPlan<'input'>;
|
|
79
|
-
readonly separator: UiDomPlan<'span'>;
|
|
80
|
-
readonly endInput: UiDomPlan<'input'>;
|
|
81
|
-
/** Popover container (hidden by default; visibility controlled by the app). */
|
|
82
|
-
readonly popover: UiDomPlan<'div'>;
|
|
83
|
-
readonly startCalendar: UiDomPlan<'div'>;
|
|
84
|
-
readonly endCalendar: UiDomPlan<'div'>;
|
|
85
|
-
}
|
|
86
|
-
export interface DateRangePickerPlanOptions {
|
|
87
|
-
readonly id: string;
|
|
88
|
-
readonly startValue?: string;
|
|
89
|
-
readonly endValue?: string;
|
|
90
|
-
readonly placeholder?: string;
|
|
91
|
-
readonly disabled?: boolean;
|
|
92
|
-
readonly className?: string;
|
|
93
|
-
readonly attrs?: UiAttributes;
|
|
94
|
-
}
|
|
95
|
-
export declare function createDateRangePickerPlans(options: DateRangePickerPlanOptions): DateRangePickerPlans;
|
package/dist/calendar.js
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
/** @license MIT
|
|
2
|
-
* Copyright (c) 2026 vobsjs
|
|
3
|
-
* @vobs/ui
|
|
4
|
-
*/
|
|
5
|
-
import { createUiDomPlan } from './primitives.js';
|
|
6
|
-
const pad2 = (value) => value.toString().padStart(2, '0');
|
|
7
|
-
/** Date → ISO 'YYYY-MM-DD' (local timezone). */
|
|
8
|
-
export function formatCalendarDate(date) {
|
|
9
|
-
return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
|
|
10
|
-
}
|
|
11
|
-
/** ISO 'YYYY-MM-DD' → local Date (returns undefined for invalid/overflow input). */
|
|
12
|
-
export function parseCalendarDate(iso) {
|
|
13
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
|
|
14
|
-
if (match === null)
|
|
15
|
-
return undefined;
|
|
16
|
-
const year = Number(match[1]);
|
|
17
|
-
const month = Number(match[2]) - 1;
|
|
18
|
-
const day = Number(match[3]);
|
|
19
|
-
if (month < 0 || month > 11 || day < 1 || day > 31)
|
|
20
|
-
return undefined;
|
|
21
|
-
const date = new Date(year, month, day);
|
|
22
|
-
// Read-back validation for overflow (e.g. 2026-13-40 is normalized by JS to 2027-02-08)
|
|
23
|
-
if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
return date;
|
|
27
|
-
}
|
|
28
|
-
/** Displayed-month parse: 'YYYY-MM' → { year, month } (invalid input falls back to the current month). */
|
|
29
|
-
function parseCalendarMonth(month) {
|
|
30
|
-
const match = month === undefined ? null : /^(\d{4})-(\d{2})$/.exec(month);
|
|
31
|
-
if (match === null) {
|
|
32
|
-
const now = new Date();
|
|
33
|
-
return { year: now.getFullYear(), month: now.getMonth() };
|
|
34
|
-
}
|
|
35
|
-
const parsed = { year: Number(match[1]), month: Number(match[2]) - 1 };
|
|
36
|
-
return parsed.month >= 0 && parsed.month <= 11
|
|
37
|
-
? parsed
|
|
38
|
-
: { year: parsed.year, month: parsed.month };
|
|
39
|
-
}
|
|
40
|
-
/** Weekday names (Sunday-first): 2024-01-07 is a Sunday; format the following 7 days via Intl. */
|
|
41
|
-
function resolveWeekdayNames(locale) {
|
|
42
|
-
const formatter = new Intl.DateTimeFormat(locale, { weekday: 'narrow' });
|
|
43
|
-
return Array.from({ length: 7 }, (_, index) => formatter.format(new Date(2024, 0, 7 + index)));
|
|
44
|
-
}
|
|
45
|
-
/** Month title format follows the locale, e.g. 'August 2026' (en-US). */
|
|
46
|
-
function resolveMonthTitle(year, month, locale) {
|
|
47
|
-
return new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'long' }).format(new Date(year, month, 1));
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Builds calendar panel plans (42-cell month view).
|
|
51
|
-
*
|
|
52
|
-
* Layout convention: Sunday-first (Chinese convention) — the grid starts on the Sunday of the week
|
|
53
|
-
* containing the displayed month's first day, for 42 cells total.
|
|
54
|
-
* Per-cell state derivation: disabled (out of min/max or whole calendar disabled) > selected > today > outside > idle.
|
|
55
|
-
*/
|
|
56
|
-
export function createCalendarPlans(options) {
|
|
57
|
-
const { id, locale = 'zh-CN' } = options;
|
|
58
|
-
const display = parseCalendarMonth(options.month);
|
|
59
|
-
const todayIso = formatCalendarDate(new Date());
|
|
60
|
-
// Start cell: back up from the first day of the displayed month to the preceding Sunday
|
|
61
|
-
const firstOfMonth = new Date(display.year, display.month, 1);
|
|
62
|
-
const start = new Date(display.year, display.month, 1 - firstOfMonth.getDay());
|
|
63
|
-
const days = [];
|
|
64
|
-
for (let index = 0; index < 42; index += 1) {
|
|
65
|
-
const date = new Date(start.getFullYear(), start.getMonth(), start.getDate() + index);
|
|
66
|
-
const iso = formatCalendarDate(date);
|
|
67
|
-
const inMonth = date.getMonth() === display.month;
|
|
68
|
-
const isDisabled = options.disabled === true ||
|
|
69
|
-
(options.min !== undefined && iso < options.min) ||
|
|
70
|
-
(options.max !== undefined && iso > options.max);
|
|
71
|
-
const isSelected = !isDisabled && iso === options.value;
|
|
72
|
-
const isToday = !isDisabled && !isSelected && iso === todayIso;
|
|
73
|
-
const dayState = isDisabled
|
|
74
|
-
? 'disabled'
|
|
75
|
-
: isSelected
|
|
76
|
-
? 'selected'
|
|
77
|
-
: isToday
|
|
78
|
-
? 'today'
|
|
79
|
-
: inMonth
|
|
80
|
-
? 'idle'
|
|
81
|
-
: 'outside';
|
|
82
|
-
days.push({
|
|
83
|
-
...createUiDomPlan('button', 'calendar-day', {
|
|
84
|
-
state: dayState,
|
|
85
|
-
disabled: isDisabled,
|
|
86
|
-
...(inMonth ? {} : { className: 'kui-calendar-day--outside' }),
|
|
87
|
-
attrs: {
|
|
88
|
-
id: `${id}-day-${index}`,
|
|
89
|
-
type: 'button',
|
|
90
|
-
'data-iso': iso,
|
|
91
|
-
'data-in-month': inMonth ? 'true' : 'false',
|
|
92
|
-
'aria-label': date.toLocaleDateString(locale),
|
|
93
|
-
'aria-pressed': isSelected ? 'true' : undefined,
|
|
94
|
-
'aria-current': isToday ? 'date' : undefined,
|
|
95
|
-
...options.attrs,
|
|
96
|
-
},
|
|
97
|
-
}),
|
|
98
|
-
day: date.getDate(),
|
|
99
|
-
iso,
|
|
100
|
-
inMonth,
|
|
101
|
-
dayState,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
const weekdays = resolveWeekdayNames(locale).map((name, index) => createUiDomPlan('span', 'calendar-weekday', {
|
|
105
|
-
attrs: {
|
|
106
|
-
id: `${id}-weekday-${index}`,
|
|
107
|
-
'data-label': name,
|
|
108
|
-
},
|
|
109
|
-
}));
|
|
110
|
-
return {
|
|
111
|
-
root: createUiDomPlan('div', 'calendar', {
|
|
112
|
-
state: options.disabled === true ? 'disabled' : 'idle',
|
|
113
|
-
...(options.className === undefined ? {} : { className: options.className }),
|
|
114
|
-
attrs: { id: `${id}-calendar`, ...options.attrs },
|
|
115
|
-
}),
|
|
116
|
-
header: createUiDomPlan('div', 'calendar-header', {
|
|
117
|
-
attrs: { id: `${id}-header` },
|
|
118
|
-
}),
|
|
119
|
-
prevButton: createUiDomPlan('button', 'calendar-nav', {
|
|
120
|
-
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
121
|
-
attrs: {
|
|
122
|
-
id: `${id}-prev`,
|
|
123
|
-
type: 'button',
|
|
124
|
-
'aria-label': 'Previous month',
|
|
125
|
-
'data-direction': 'prev',
|
|
126
|
-
},
|
|
127
|
-
}),
|
|
128
|
-
nextButton: createUiDomPlan('button', 'calendar-nav', {
|
|
129
|
-
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
130
|
-
attrs: {
|
|
131
|
-
id: `${id}-next`,
|
|
132
|
-
type: 'button',
|
|
133
|
-
'aria-label': 'Next month',
|
|
134
|
-
'data-direction': 'next',
|
|
135
|
-
},
|
|
136
|
-
}),
|
|
137
|
-
title: createUiDomPlan('span', 'calendar-title', {
|
|
138
|
-
attrs: {
|
|
139
|
-
id: `${id}-title`,
|
|
140
|
-
'data-label': resolveMonthTitle(display.year, display.month, locale),
|
|
141
|
-
},
|
|
142
|
-
}),
|
|
143
|
-
weekdayRow: createUiDomPlan('div', 'calendar-weekday-row', {
|
|
144
|
-
attrs: { id: `${id}-weekday-row` },
|
|
145
|
-
}),
|
|
146
|
-
weekdays,
|
|
147
|
-
grid: createUiDomPlan('div', 'calendar-grid', {
|
|
148
|
-
attrs: { id: `${id}-grid`, role: 'grid' },
|
|
149
|
-
}),
|
|
150
|
-
days,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
export function createDateRangePickerPlans(options) {
|
|
154
|
-
const placeholder = options.placeholder ?? 'YYYY-MM-DD';
|
|
155
|
-
return {
|
|
156
|
-
root: createUiDomPlan('div', 'date-range-picker', {
|
|
157
|
-
...(options.className === undefined ? {} : { className: options.className }),
|
|
158
|
-
attrs: { id: `${options.id}-range`, ...options.attrs },
|
|
159
|
-
}),
|
|
160
|
-
startInput: createUiDomPlan('input', 'date-range-input', {
|
|
161
|
-
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
162
|
-
attrs: {
|
|
163
|
-
id: `${options.id}-start`,
|
|
164
|
-
type: 'text',
|
|
165
|
-
readonly: 'true',
|
|
166
|
-
...(options.startValue === undefined ? {} : { value: options.startValue }),
|
|
167
|
-
...(options.placeholder === undefined ? {} : { placeholder }),
|
|
168
|
-
},
|
|
169
|
-
}),
|
|
170
|
-
separator: createUiDomPlan('span', 'date-range-separator', {
|
|
171
|
-
attrs: { id: `${options.id}-sep`, 'data-label': '~' },
|
|
172
|
-
}),
|
|
173
|
-
endInput: createUiDomPlan('input', 'date-range-input', {
|
|
174
|
-
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
175
|
-
attrs: {
|
|
176
|
-
id: `${options.id}-end`,
|
|
177
|
-
type: 'text',
|
|
178
|
-
readonly: 'true',
|
|
179
|
-
...(options.endValue === undefined ? {} : { value: options.endValue }),
|
|
180
|
-
...(options.placeholder === undefined ? {} : { placeholder }),
|
|
181
|
-
},
|
|
182
|
-
}),
|
|
183
|
-
popover: createUiDomPlan('div', 'date-range-popover', {
|
|
184
|
-
state: 'hidden',
|
|
185
|
-
attrs: { id: `${options.id}-popover`, role: 'dialog', 'aria-hidden': 'true' },
|
|
186
|
-
}),
|
|
187
|
-
startCalendar: createUiDomPlan('div', 'date-range-calendar', {
|
|
188
|
-
attrs: { id: `${options.id}-start-calendar` },
|
|
189
|
-
}),
|
|
190
|
-
endCalendar: createUiDomPlan('div', 'date-range-calendar', {
|
|
191
|
-
attrs: { id: `${options.id}-end-calendar` },
|
|
192
|
-
}),
|
|
193
|
-
};
|
|
194
|
-
}
|