@vobs/ui 1.3.1 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/toast.cjs CHANGED
@@ -25,12 +25,13 @@ __export(toast_exports, {
25
25
  ToastHost: () => ToastHost
26
26
  });
27
27
  module.exports = __toCommonJS(toast_exports);
28
- var import_vobs3 = require("@vobs/vobs");
29
28
  var import_vobs4 = require("@vobs/vobs");
29
+ var import_vobs5 = require("@vobs/vobs");
30
30
  var import_notification = require("@vobs/notification");
31
31
 
32
32
  // packages/ui/src/icon.ts
33
33
  var import_icon_core = require("@vobs/icon-core");
34
+ var import_vobs2 = require("@vobs/vobs");
34
35
 
35
36
  // packages/ui/src/utils.ts
36
37
  var import_reactivity = require("@vobs/reactivity");
@@ -150,12 +151,15 @@ __name(removeAttribute, "removeAttribute");
150
151
  // packages/ui/src/icon.ts
151
152
  function Icon(props = {}) {
152
153
  const root = (0, import_icon_core.createSvgIconNode)(() => {
154
+ registryVersion.value;
153
155
  const definition = resolveIcon(
154
156
  readProp(props, "icon", void 0) ?? readProp(props, "name", void 0)
155
157
  );
156
158
  if (!definition) {
157
159
  if (hasProp(props, "children")) return void 0;
158
- throw new Error("Vobs UI: Icon requires a registered `name` or an `icon` definition");
160
+ const requested = readProp(props, "icon", void 0) ?? readProp(props, "name", void 0);
161
+ console.warn(`Vobs UI: \u672A\u6CE8\u518C\u7684\u56FE\u6807 "${String(requested)}"\uFF0C\u5DF2\u6E32\u67D3\u4E3A\u7A7A\u3002\u8BF7\u5148 registerIcon \u6216\u6539\u7528\u5DF2\u6CE8\u518C\u7684\u540D\u79F0\u3002`);
162
+ return void 0;
159
163
  }
160
164
  return iconDefinitionToSvg(definition);
161
165
  }, props, {
@@ -229,6 +233,7 @@ var VUI_ICON_PATHS = {
229
233
  var iconRegistry = new Map(
230
234
  Object.entries(VUI_ICON_PATHS).map(([name, path]) => [name, { name, path }])
231
235
  );
236
+ var registryVersion = (0, import_vobs2.state)(0, "vui.iconRegistry.version");
232
237
  function resolveIcon(icon) {
233
238
  return typeof icon === "string" ? iconRegistry.get(icon) : icon;
234
239
  }
@@ -236,9 +241,9 @@ __name(resolveIcon, "resolveIcon");
236
241
 
237
242
  // packages/ui/src/overlay.ts
238
243
  var import_reactivity2 = require("@vobs/reactivity");
239
- var import_vobs2 = require("@vobs/vobs");
244
+ var import_vobs3 = require("@vobs/vobs");
240
245
  function createPortal(node, adapter, target) {
241
- const placeholder = (0, import_vobs2.createComment)("vui:portal");
246
+ const placeholder = (0, import_vobs3.createComment)("vui:portal");
242
247
  const owner = (0, import_reactivity2.getCurrentOwner)();
243
248
  if (!owner) throw new Error("Vobs UI: createPortal \u5FC5\u987B\u5728\u7EC4\u4EF6 Owner \u4F5C\u7528\u57DF\u5185\u8C03\u7528");
244
249
  adapter.mount(node, target);
@@ -264,45 +269,45 @@ var FOCUSABLE_SELECTOR = [
264
269
  // packages/ui/src/toast.ts
265
270
  function ToastHost(props = {}) {
266
271
  const notification = props.notification ?? (0, import_notification.useNotification)();
267
- const root = (0, import_vobs4.createElement)("ol");
272
+ const root = (0, import_vobs5.createElement)("ol");
268
273
  bindClassList(root, props, () => [
269
274
  "vui-toast-host",
270
275
  `vui-toast-host--${readProp(props, "position", "top-right")}`
271
276
  ]);
272
277
  bindCommonAttributes(root, props, ["notification", "position", "closeLabel", "portal", "portalTarget"]);
273
278
  bindUserStyle(root, props);
274
- (0, import_vobs4.setAttribute)(root, "aria-label", "Notifications");
275
- (0, import_vobs4.setAttribute)(root, "role", "region");
276
- (0, import_vobs3.insertList)(root, null, () => notification.notifications.value, (entry) => createToast(entry, notification, readProp(props, "closeLabel", "Dismiss notification")), (entry) => entry.id);
279
+ (0, import_vobs5.setAttribute)(root, "aria-label", "Notifications");
280
+ (0, import_vobs5.setAttribute)(root, "role", "region");
281
+ (0, import_vobs4.insertList)(root, null, () => notification.notifications.value, (entry) => createToast(entry, notification, readProp(props, "closeLabel", "Dismiss notification")), (entry) => entry.id);
277
282
  const portal = readProp(props, "portal", void 0);
278
283
  return portal ? createPortal(root, portal, readProp(props, "portalTarget", void 0)) : root;
279
284
  }
280
285
  __name(ToastHost, "ToastHost");
281
286
  function createToast(notification, context, closeLabel) {
282
- const item = (0, import_vobs4.createElement)("li");
283
- const content = (0, import_vobs4.createElement)("div");
284
- const close = (0, import_vobs4.createElement)("button");
285
- (0, import_vobs4.setAttribute)(item, "class", `vui-toast vui-toast--${notification.type}`);
286
- (0, import_vobs4.setAttribute)(item, "data-notification-id", notification.id);
287
- (0, import_vobs4.setAttribute)(item, "role", notification.type === "error" ? "alert" : "status");
288
- (0, import_vobs4.setAttribute)(content, "class", "vui-toast__content");
289
- (0, import_vobs4.setAttribute)(close, "class", "vui-toast__close");
290
- (0, import_vobs4.setAttribute)(close, "type", "button");
291
- (0, import_vobs4.setAttribute)(close, "aria-label", closeLabel);
292
- (0, import_vobs4.addEventListener)(close, "click", () => context.dismiss(notification.id));
287
+ const item = (0, import_vobs5.createElement)("li");
288
+ const content = (0, import_vobs5.createElement)("div");
289
+ const close = (0, import_vobs5.createElement)("button");
290
+ (0, import_vobs5.setAttribute)(item, "class", `vui-toast vui-toast--${notification.type}`);
291
+ (0, import_vobs5.setAttribute)(item, "data-notification-id", notification.id);
292
+ (0, import_vobs5.setAttribute)(item, "role", notification.type === "error" ? "alert" : "status");
293
+ (0, import_vobs5.setAttribute)(content, "class", "vui-toast__content");
294
+ (0, import_vobs5.setAttribute)(close, "class", "vui-toast__close");
295
+ (0, import_vobs5.setAttribute)(close, "type", "button");
296
+ (0, import_vobs5.setAttribute)(close, "aria-label", closeLabel);
297
+ (0, import_vobs5.addEventListener)(close, "click", () => context.dismiss(notification.id));
293
298
  if (notification.title) {
294
- const title = (0, import_vobs4.createElement)("div");
295
- (0, import_vobs4.setAttribute)(title, "class", "vui-toast__title");
296
- (0, import_vobs4.insertBefore)(title, (0, import_vobs4.createText)(notification.title), null);
297
- (0, import_vobs4.insertBefore)(content, title, null);
299
+ const title = (0, import_vobs5.createElement)("div");
300
+ (0, import_vobs5.setAttribute)(title, "class", "vui-toast__title");
301
+ (0, import_vobs5.insertBefore)(title, (0, import_vobs5.createText)(notification.title), null);
302
+ (0, import_vobs5.insertBefore)(content, title, null);
298
303
  }
299
- const message = (0, import_vobs4.createElement)("div");
300
- (0, import_vobs4.setAttribute)(message, "class", "vui-toast__message");
301
- (0, import_vobs4.insertBefore)(message, (0, import_vobs4.createText)(notification.content), null);
302
- (0, import_vobs4.insertBefore)(content, message, null);
303
- (0, import_vobs4.insertBefore)(close, Icon({ name: "x", size: 16, decorative: true }), null);
304
- (0, import_vobs4.insertBefore)(item, content, null);
305
- (0, import_vobs4.insertBefore)(item, close, null);
304
+ const message = (0, import_vobs5.createElement)("div");
305
+ (0, import_vobs5.setAttribute)(message, "class", "vui-toast__message");
306
+ (0, import_vobs5.insertBefore)(message, (0, import_vobs5.createText)(notification.content), null);
307
+ (0, import_vobs5.insertBefore)(content, message, null);
308
+ (0, import_vobs5.insertBefore)(close, Icon({ name: "x", size: 16, decorative: true }), null);
309
+ (0, import_vobs5.insertBefore)(item, content, null);
310
+ (0, import_vobs5.insertBefore)(item, close, null);
306
311
  return item;
307
312
  }
308
313
  __name(createToast, "createToast");
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/toast.ts","../src/icon.ts","../src/utils.ts","../src/overlay.ts"],"sourcesContent":["import { insertList } from '@vobs/vobs'\nimport {\n addEventListener,\n createElement,\n createText,\n insertBefore,\n setAttribute,\n type VobsNode\n} from '@vobs/vobs'\nimport { type Notification, type NotificationContext, useNotification } from '@vobs/notification'\nimport { Icon } from './icon'\nimport { createPortal, type VuiPortalAdapter } from './overlay'\nimport { bindClassList, bindCommonAttributes, bindUserStyle, readProp } from './utils'\nimport type { VuiCommonProps } from './types'\n\nexport type ToastPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n\nexport interface ToastHostProps extends VuiCommonProps {\n readonly notification?: NotificationContext\n readonly position?: ToastPosition\n readonly closeLabel?: string\n readonly portal?: VuiPortalAdapter<unknown>\n readonly portalTarget?: unknown\n}\n\nexport function ToastHost(props: ToastHostProps = {}): VobsNode {\n const notification = props.notification ?? useNotification()\n const root = createElement('ol')\n bindClassList(root, props, () => [\n 'vui-toast-host',\n `vui-toast-host--${readProp<ToastPosition>(props, 'position', 'top-right')}`\n ])\n bindCommonAttributes(root, props, ['notification', 'position', 'closeLabel', 'portal', 'portalTarget'])\n bindUserStyle(root, props)\n setAttribute(root, 'aria-label', 'Notifications')\n setAttribute(root, 'role', 'region')\n\n insertList(root, null, () => notification.notifications.value, entry => (\n createToast(entry, notification, readProp(props, 'closeLabel', 'Dismiss notification'))\n ), entry => entry.id)\n\n const portal = readProp<VuiPortalAdapter<unknown> | undefined>(props, 'portal', undefined)\n return portal\n ? createPortal(root, portal, readProp(props, 'portalTarget', undefined))\n : root\n}\n\nfunction createToast(\n notification: Notification,\n context: NotificationContext,\n closeLabel: string\n): VobsNode {\n const item = createElement('li')\n const content = createElement('div')\n const close = createElement('button')\n setAttribute(item, 'class', `vui-toast vui-toast--${notification.type}`)\n setAttribute(item, 'data-notification-id', notification.id)\n setAttribute(item, 'role', notification.type === 'error' ? 'alert' : 'status')\n setAttribute(content, 'class', 'vui-toast__content')\n setAttribute(close, 'class', 'vui-toast__close')\n setAttribute(close, 'type', 'button')\n setAttribute(close, 'aria-label', closeLabel)\n addEventListener(close, 'click', () => context.dismiss(notification.id))\n\n if (notification.title) {\n const title = createElement('div')\n setAttribute(title, 'class', 'vui-toast__title')\n insertBefore(title, createText(notification.title), null)\n insertBefore(content, title, null)\n }\n const message = createElement('div')\n setAttribute(message, 'class', 'vui-toast__message')\n insertBefore(message, createText(notification.content), null)\n insertBefore(content, message, null)\n insertBefore(close, Icon({ name: 'x', size: 16, decorative: true }), null)\n insertBefore(item, content, null)\n insertBefore(item, close, null)\n return item\n}\n","import {\n createIcon,\n createSvgIconNode,\n type IconDefinition,\n type SvgIconDefinition,\n type VobsIconProps\n} from '@vobs/icon-core'\nimport { hasProp, mountSlot, readProp } from './utils'\nimport type { VobsNode } from '@vobs/vobs'\nimport type { VuiCommonProps } from './types'\n\nexport type { IconDefinition } from '@vobs/icon-core'\n\nexport interface IconProps extends VuiCommonProps, VobsIconProps {\n readonly name?: string\n readonly icon?: IconDefinition\n}\n\nexport function Icon(props: IconProps = {}): VobsNode {\n const root = createSvgIconNode(() => {\n const definition = resolveIcon(\n readProp<IconDefinition | string | undefined>(props, 'icon', undefined)\n ?? readProp<string | undefined>(props, 'name', undefined)\n )\n if (!definition) {\n if (hasProp(props, 'children')) return undefined\n throw new Error('Vobs UI: Icon requires a registered `name` or an `icon` definition')\n }\n return iconDefinitionToSvg(definition)\n }, props, {\n class: 'vui-icon icon',\n defaultSvgAttributes: {\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': 2,\n 'stroke-linecap': 'butt',\n 'stroke-linejoin': 'miter'\n }\n }) as Element\n\n if (hasProp(props, 'children')) mountSlot(root, props, 'children')\n return root\n}\n\nfunction iconDefinitionToSvg(definition: IconDefinition): SvgIconDefinition {\n return {\n name: definition.name,\n body: definition.path,\n viewBox: definition.viewBox\n }\n}\n\nconst VUI_ICON_PATHS = {\n search: '<circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m20 20-3.5-3.5\"/>',\n check: '<polyline points=\"4 12 10 18 20 6\"/>',\n x: '<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>',\n plus: '<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n minus: '<line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n 'chevron-right': '<polyline points=\"9 6 15 12 9 18\"/>',\n 'chevron-down': '<polyline points=\"6 9 12 15 18 9\"/>',\n 'chevron-up': '<polyline points=\"6 15 12 9 18 15\"/>',\n 'chevron-left': '<polyline points=\"15 6 9 12 15 18\"/>',\n 'arrow-left': '<line x1=\"20\" y1=\"12\" x2=\"4\" y2=\"12\"/><polyline points=\"10 6 4 12 10 18\"/>',\n 'arrow-right': '<line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/><polyline points=\"14 6 20 12 14 18\"/>',\n 'arrow-up': '<line x1=\"12\" y1=\"20\" x2=\"12\" y2=\"4\"/><polyline points=\"6 10 12 4 18 10\"/>',\n 'arrow-down': '<line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/><polyline points=\"6 14 12 20 18 14\"/>',\n atom: '<circle cx=\"12\" cy=\"12\" r=\"2\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\" transform=\"rotate(60 12 12)\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\" transform=\"rotate(120 12 12)\"/>',\n globe: '<circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18\"/>',\n 'arrow-expand': '<polyline points=\"14 4 20 4 20 10\"/><line x1=\"14\" y1=\"10\" x2=\"20\" y2=\"4\"/><polyline points=\"10 20 4 20 4 14\"/><line x1=\"10\" y1=\"14\" x2=\"4\" y2=\"20\"/>',\n 'arrow-minimize': '<polyline points=\"20 4 14 10 20 10\"/><line x1=\"14\" y1=\"10\" x2=\"14\" y2=\"4\"/><polyline points=\"4 20 10 14 4 14\"/><line x1=\"10\" y1=\"14\" x2=\"10\" y2=\"20\"/>',\n 'check-circle': '<circle cx=\"12\" cy=\"12\" r=\"9\"/><polyline points=\"8 12 11 15 16 9\"/>',\n 'alert-circle': '<circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"16.01\"/>',\n 'alert-triangle': '<path d=\"M12 3 22 20 2 20 Z\"/><line x1=\"12\" y1=\"10\" x2=\"12\" y2=\"15\"/><line x1=\"12\" y1=\"18\" x2=\"12\" y2=\"18.01\"/>',\n info: '<circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"8.01\"/>',\n home: '<polygon points=\"3 11 12 3 21 11 21 21 14 21 14 14 10 14 10 21 3 21 3 11\"/>',\n user: '<path d=\"M4 21v-1a6 6 0 0 1 6-6h4a6 6 0 0 1 6 6v1\"/><circle cx=\"12\" cy=\"8\" r=\"4\"/>',\n settings: '<circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z\"/>',\n menu: '<line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/><line x1=\"4\" y1=\"18\" x2=\"20\" y2=\"18\"/>',\n 'more-h': '<circle cx=\"5\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"19\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/>',\n calendar: '<rect x=\"3\" y=\"5\" width=\"18\" height=\"16\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/><line x1=\"8\" y1=\"3\" x2=\"8\" y2=\"7\"/><line x1=\"16\" y1=\"3\" x2=\"16\" y2=\"7\"/>',\n filter: '<polygon points=\"3 4 21 4 14 12 14 20 10 18 10 12 3 4\"/>',\n sort: '<path d=\"M8 20V4\"/><polyline points=\"4 8 8 4 12 8\"/><path d=\"M16 4v16\"/><polyline points=\"12 16 16 20 20 16\"/>',\n edit: '<path d=\"M4 20h4l10-10-4-4L4 16v4z\"/><path d=\"M14 6l4 4\"/>',\n trash: '<polyline points=\"4 6 20 6\"/><path d=\"M6 6v14h12V6\"/><path d=\"M9 6V4h6v2\"/><line x1=\"10\" y1=\"10\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"10\" x2=\"14\" y2=\"17\"/>',\n download: '<path d=\"M12 3v12\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"21\"/>',\n upload: '<path d=\"M12 21V6\"/><polyline points=\"7 11 12 6 17 11\"/><line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"21\"/>',\n pause: '<line x1=\"9\" y1=\"5\" x2=\"9\" y2=\"19\"/><line x1=\"15\" y1=\"5\" x2=\"15\" y2=\"19\"/>',\n play: '<polygon points=\"8 5 19 12 8 19 8 5\"/>',\n refresh: '<polyline points=\"21 4 21 10 15 10\"/><polyline points=\"3 20 3 14 9 14\"/><path d=\"M20.5 9A9 9 0 0 0 5 5.5L3 7M3.5 15A9 9 0 0 0 19 18.5L21 17\"/>',\n bell: '<path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 8 3 8H3s3-1 3-8z\"/><path d=\"M10 21a2 2 0 0 0 4 0\"/>',\n folder: '<path d=\"M3 6h6l2 3h10v10H3z\"/>',\n code: '<polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/>',\n sparkles: '<path d=\"M12 3l1.8 4.2L18 9l-4.2 1.8L12 15l-1.8-4.2L6 9l4.2-1.8L12 3z\"/><path d=\"M19 14l1 2.2 2.2 1-2.2 1L19 20.4l-1-2.2-2.2-1 2.2-1L19 14z\"/>',\n zap: '<polygon points=\"13 2 4 14 12 14 11 22 20 10 12 10 13 2\"/>',\n sun: '<circle cx=\"12\" cy=\"12\" r=\"4\"/><line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"5\"/><line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"22\"/><line x1=\"2\" y1=\"12\" x2=\"5\" y2=\"12\"/><line x1=\"19\" y1=\"12\" x2=\"22\" y2=\"12\"/><line x1=\"4.6\" y1=\"4.6\" x2=\"6.7\" y2=\"6.7\"/><line x1=\"17.3\" y1=\"17.3\" x2=\"19.4\" y2=\"19.4\"/><line x1=\"4.6\" y1=\"19.4\" x2=\"6.7\" y2=\"17.3\"/><line x1=\"17.3\" y1=\"6.7\" x2=\"19.4\" y2=\"4.6\"/>',\n moon: '<path d=\"M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z\"/>',\n terminal: '<polyline points=\"4 7 9 12 4 17\"/><line x1=\"12\" y1=\"17\" x2=\"20\" y2=\"17\"/>'\n} as const\n\nconst iconRegistry = new Map<string, IconDefinition>(\n Object.entries(VUI_ICON_PATHS).map(([name, path]) => [name, { name, path }])\n)\n\nexport { VUI_ICON_PATHS }\nexport { createIcon, registerIcon, resolveIcon }\n\nfunction registerIcon(icon: IconDefinition): () => void {\n const previous = iconRegistry.get(icon.name)\n iconRegistry.set(icon.name, icon)\n return () => {\n if (previous) iconRegistry.set(icon.name, previous)\n else iconRegistry.delete(icon.name)\n }\n}\n\nfunction resolveIcon(icon: string | IconDefinition | undefined): IconDefinition | undefined {\n return typeof icon === 'string' ? iconRegistry.get(icon) : icon\n}\n","import { effect } from '@vobs/reactivity'\nimport {\n addEventListener,\n createFragment,\n createText,\n insertBefore,\n insertDynamic,\n setAttribute,\n setProperty,\n setTextContent,\n type VobsNode\n} from '@vobs/vobs'\nimport type { VuiCommonProps } from './types'\n\ntype VuiProps = VuiCommonProps | object\n\nexport function classNames(...values: readonly (string | false | null | undefined)[]): string {\n return values\n .flatMap(value => typeof value === 'string' ? value.trim().split(/\\s+/u) : [])\n .filter(Boolean)\n .join(' ')\n}\n\nexport function readProp<T>(props: object, name: string, fallback: T): T {\n const value = Reflect.get(props, name)\n return (value === undefined ? fallback : value) as T\n}\n\nexport function hasProp(props: object, name: string): boolean {\n return name in props\n}\n\nexport function bindClass(\n root: Element,\n props: VuiProps,\n ...baseClasses: readonly (string | undefined)[]\n): void {\n effect(() => {\n setAttribute(root, 'class', classNames(\n ...baseClasses,\n readString(props, 'class'),\n readString(props, 'className')\n ))\n })\n}\n\nexport function bindClassList(\n root: Element,\n props: VuiProps,\n readBaseClasses: () => readonly (string | undefined)[]\n): void {\n effect(() => {\n setAttribute(root, 'class', classNames(\n ...readBaseClasses(),\n readString(props, 'class'),\n readString(props, 'className')\n ))\n })\n}\n\nexport function bindCommonAttributes(\n root: Element,\n props: VuiProps,\n skip: readonly string[] = [],\n options: { readonly includeDataAria?: boolean } = {}\n): void {\n const ignored = new Set(skip)\n ignored.add('class')\n ignored.add('className')\n ignored.add('children')\n ignored.add('style')\n\n effect(() => {\n const next = new Map<string, string>()\n for (const name of Object.keys(props)) {\n if (ignored.has(name) || name.startsWith('on')) continue\n if (!isCommonAttribute(name)) continue\n if (options.includeDataAria === false && (name.startsWith('aria-') || name.startsWith('data-'))) continue\n\n const value = Reflect.get(props, name)\n const normalized = normalizeAttribute(name, value)\n if (normalized !== undefined) next.set(normalized.name, normalized.value)\n }\n\n const managed = getManagedAttributes(root)\n for (const name of managed) {\n if (!next.has(name)) removeAttribute(root, name)\n }\n for (const [name, value] of next) setAttribute(root, name, value)\n setManagedAttributes(root, next.keys())\n })\n}\n\nexport function bindUserStyle(root: Element, props: VuiProps): void {\n bindStyle(root, props, () => '')\n}\n\nexport function bindStyle(root: Element, props: VuiProps, createStyle: () => string): void {\n effect(() => {\n const userStyle = readString(props, 'style')\n const internalStyle = createStyle()\n const style = [internalStyle, userStyle].filter(Boolean).join('; ')\n if (style) setAttribute(root, 'style', style)\n else removeAttribute(root, 'style')\n })\n}\n\nexport function bindTextContent(\n node: Text,\n read: () => unknown\n): void {\n effect(() => {\n setTextContent(node, String(read() ?? ''))\n })\n}\n\nexport function bindPropertyValue(\n node: Element,\n name: string,\n read: () => unknown\n): void {\n effect(() => setProperty(node, name, read()))\n}\n\nexport function bindAttributeValue(\n node: Element,\n name: string,\n read: () => unknown\n): void {\n effect(() => {\n const value = read()\n if (value === undefined || value === null || value === false) removeAttribute(node, name)\n else setAttribute(node, name, String(value))\n })\n}\n\nexport function listen(\n root: Element,\n event: string,\n props: object,\n propName: string,\n disabled?: () => boolean\n): void {\n addEventListener(root, event, (reason: Event) => {\n if (disabled?.()) return\n const handler = Reflect.get(props, propName)\n if (typeof handler === 'function') handler(reason)\n })\n}\n\nexport function mountSlot(\n parent: Node,\n props: object,\n propName: string\n): void {\n insertDynamic(parent, null, () => resolveSlot(Reflect.get(props, propName)))\n}\n\nexport function resolveSlot(value: unknown): VobsNode | null {\n const resolved = typeof value === 'function' ? value() : value\n if (resolved === undefined || resolved === null || resolved === false) return null\n if (typeof resolved === 'string' || typeof resolved === 'number') return createText(String(resolved))\n if (Array.isArray(resolved)) {\n const children = resolved\n return createFragment((parent, anchor) => {\n for (const child of children) {\n const node = resolveSlot(child)\n if (node) insertBefore(parent, node, anchor)\n }\n })\n }\n return resolved as VobsNode\n}\n\nexport function setOptionalAttribute(node: Element, name: string, value: unknown): void {\n if (value === undefined || value === null || value === false || value === '') {\n removeAttribute(node, name)\n return\n }\n setAttribute(node, name, String(value))\n}\n\nexport function setOptionalProperty(node: Element, name: string, value: unknown): void {\n if (value === undefined || value === null) return\n setProperty(node, name, value)\n}\n\nfunction readString(props: object, name: string): string | undefined {\n const value = Reflect.get(props, name)\n return typeof value === 'string' ? value : undefined\n}\n\nfunction isCommonAttribute(name: string): boolean {\n return name === 'id'\n || name === 'title'\n || name === 'role'\n || name === 'tabIndex'\n || name.startsWith('aria-')\n || name.startsWith('data-')\n}\n\nfunction normalizeAttribute(name: string, value: unknown): { name: string; value: string } | undefined {\n if (value === undefined || value === null) return undefined\n if (value === false && !name.startsWith('aria-') && !name.startsWith('data-')) return undefined\n return { name: name === 'tabIndex' ? 'tabindex' : name, value: String(value) }\n}\n\nconst managedAttributes = new WeakMap<object, Set<string>>()\n\nfunction getManagedAttributes(node: Element): Set<string> {\n return managedAttributes.get(node) ?? new Set<string>()\n}\n\nfunction setManagedAttributes(node: Element, names: IterableIterator<string>): void {\n managedAttributes.set(node, new Set(names))\n}\n\nfunction removeAttribute(node: Element, name: string): void {\n const candidate = node as Element & { removeAttribute?: (attribute: string) => void }\n candidate.removeAttribute?.(name)\n}\n","import { getCurrentOwner, onDispose } from '@vobs/reactivity'\nimport {\n createComment,\n insertBefore,\n isVobsFragment,\n type VobsNode\n} from '@vobs/vobs'\n\nexport interface VuiPortalAdapter<Target = unknown> {\n mount(node: VobsNode, target: Target): void\n unmount(node: VobsNode, target: Target): void\n}\n\nexport function createPortal<Target>(\n node: VobsNode,\n adapter: VuiPortalAdapter<Target>,\n target: Target\n): VobsNode {\n const placeholder = createComment('vui:portal')\n const owner = getCurrentOwner()\n if (!owner) throw new Error('Vobs UI: createPortal 必须在组件 Owner 作用域内调用')\n adapter.mount(node, target)\n const cleanup = (): void => adapter.unmount(node, target)\n owner.onDispose(cleanup)\n return placeholder\n}\n\nexport function createDOMPortalAdapter(): VuiPortalAdapter<Node> {\n return {\n mount(node, target): void {\n insertBefore(target, node, null)\n },\n unmount(node, target): void {\n removePortalNode(node, target)\n }\n }\n}\n\nexport interface FocusTrapOptions {\n readonly initialFocus?: HTMLElement | (() => HTMLElement | null)\n readonly restoreFocus?: boolean\n readonly onEscape?: (event: KeyboardEvent) => void\n}\n\nexport interface FocusTrapHandle {\n readonly active: boolean\n activate(): void\n deactivate(): void\n dispose(): void\n}\n\nexport function createFocusTrap(\n root: HTMLElement,\n options: FocusTrapOptions = {}\n): FocusTrapHandle {\n let isActive = false\n let disposed = false\n let previouslyFocused: Element | null = null\n\n const focusable = (): HTMLElement[] => [...root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)]\n .filter(element => !element.hasAttribute('disabled') && element.getAttribute('aria-hidden') !== 'true')\n\n const onKeyDown = (event: KeyboardEvent): void => {\n if (!isActive || disposed) return\n if (event.key === 'Escape') {\n options.onEscape?.(event)\n return\n }\n if (event.key !== 'Tab') return\n const elements = focusable()\n if (elements.length === 0) {\n event.preventDefault()\n root.focus()\n return\n }\n const first = elements[0]\n const last = elements[elements.length - 1]\n const current = document.activeElement\n if (event.shiftKey && current === first) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && current === last) {\n event.preventDefault()\n first.focus()\n }\n }\n\n const activate = (): void => {\n if (disposed || isActive) return\n isActive = true\n previouslyFocused = document.activeElement\n root.addEventListener('keydown', onKeyDown)\n const initial = resolveInitialFocus(options.initialFocus) ?? focusable()[0] ?? root\n if (!root.hasAttribute('tabindex') && initial === root) root.setAttribute('tabindex', '-1')\n initial.focus()\n }\n\n const deactivate = (): void => {\n if (!isActive) return\n isActive = false\n root.removeEventListener('keydown', onKeyDown)\n if (options.restoreFocus !== false\n && typeof HTMLElement !== 'undefined'\n && previouslyFocused instanceof HTMLElement) previouslyFocused.focus()\n previouslyFocused = null\n }\n\n const handle: FocusTrapHandle = {\n get active(): boolean { return isActive },\n activate,\n deactivate,\n dispose(): void {\n if (disposed) return\n deactivate()\n disposed = true\n }\n }\n if (getCurrentOwner()) onDispose(() => handle.dispose())\n return handle\n}\n\nexport interface OverlayEntry {\n readonly id: string\n readonly modal?: boolean\n readonly closeOnEscape?: boolean\n readonly onEscape?: () => void\n}\n\nexport interface OverlayManager {\n readonly entries: readonly OverlayEntry[]\n open(entry: OverlayEntry): void\n close(id: string): void\n bringToFront(id: string): void\n subscribe(listener: (entries: readonly OverlayEntry[]) => void): () => void\n}\n\nexport function createOverlayManager(): OverlayManager {\n let entries: OverlayEntry[] = []\n const listeners = new Set<(entries: readonly OverlayEntry[]) => void>()\n const notify = (): void => {\n const snapshot = entries.slice()\n for (const listener of listeners) listener(snapshot)\n }\n\n return {\n get entries(): readonly OverlayEntry[] { return entries },\n open(entry): void {\n entries = [...entries.filter(current => current.id !== entry.id), entry]\n notify()\n },\n close(id): void {\n const next = entries.filter(entry => entry.id !== id)\n if (next.length === entries.length) return\n entries = next\n notify()\n },\n bringToFront(id): void {\n const entry = entries.find(current => current.id === id)\n if (!entry || entries[entries.length - 1] === entry) return\n entries = [...entries.filter(current => current !== entry), entry]\n notify()\n },\n subscribe(listener): () => void {\n listeners.add(listener)\n listener(entries.slice())\n return () => listeners.delete(listener)\n }\n }\n}\n\nfunction resolveInitialFocus(\n target: FocusTrapOptions['initialFocus']\n): HTMLElement | null {\n if (!target) return null\n return typeof target === 'function' ? target() : target\n}\n\nfunction removePortalNode(node: VobsNode, target: Node): void {\n if (isVobsFragment(node)) {\n let current = node.start.nextSibling\n while (current && current !== node.end) {\n const next = current.nextSibling\n target.removeChild(current)\n current = next\n }\n if (node.start.parentNode === target) target.removeChild(node.start)\n if (node.end.parentNode === target) target.removeChild(node.end)\n return\n }\n if (node.parentNode === target) target.removeChild(node)\n}\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button',\n 'input',\n 'select',\n 'textarea',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]'\n].join(',')\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,eAA2B;AAC3B,IAAAA,eAOO;AACP,0BAA6E;;;ACT7E,uBAMO;;;ACNP,wBAAuB;AACvB,kBAUO;AAKA,SAAS,cAAc,QAAgE;AAC5F,SAAO,OACJ,QAAQ,WAAS,OAAO,UAAU,WAAW,MAAM,KAAK,EAAE,MAAM,MAAM,IAAI,CAAC,CAAC,EAC5E,OAAO,OAAO,EACd,KAAK,GAAG;AACb;AALgB;AAOT,SAAS,SAAY,OAAe,MAAc,UAAgB;AACvE,QAAM,QAAQ,QAAQ,IAAI,OAAO,IAAI;AACrC,SAAQ,UAAU,SAAY,WAAW;AAC3C;AAHgB;AAKT,SAAS,QAAQ,OAAe,MAAuB;AAC5D,SAAO,QAAQ;AACjB;AAFgB;AAkBT,SAAS,cACd,MACA,OACA,iBACM;AACN,gCAAO,MAAM;AACX,kCAAa,MAAM,SAAS;AAAA,MAC1B,GAAG,gBAAgB;AAAA,MACnB,WAAW,OAAO,OAAO;AAAA,MACzB,WAAW,OAAO,WAAW;AAAA,IAC/B,CAAC;AAAA,EACH,CAAC;AACH;AAZgB;AAcT,SAAS,qBACd,MACA,OACA,OAA0B,CAAC,GAC3B,UAAkD,CAAC,GAC7C;AACN,QAAM,UAAU,IAAI,IAAI,IAAI;AAC5B,UAAQ,IAAI,OAAO;AACnB,UAAQ,IAAI,WAAW;AACvB,UAAQ,IAAI,UAAU;AACtB,UAAQ,IAAI,OAAO;AAEnB,gCAAO,MAAM;AACX,UAAM,OAAO,oBAAI,IAAoB;AACrC,eAAW,QAAQ,OAAO,KAAK,KAAK,GAAG;AACrC,UAAI,QAAQ,IAAI,IAAI,KAAK,KAAK,WAAW,IAAI,EAAG;AAChD,UAAI,CAAC,kBAAkB,IAAI,EAAG;AAC9B,UAAI,QAAQ,oBAAoB,UAAU,KAAK,WAAW,OAAO,KAAK,KAAK,WAAW,OAAO,GAAI;AAEjG,YAAM,QAAQ,QAAQ,IAAI,OAAO,IAAI;AACrC,YAAM,aAAa,mBAAmB,MAAM,KAAK;AACjD,UAAI,eAAe,OAAW,MAAK,IAAI,WAAW,MAAM,WAAW,KAAK;AAAA,IAC1E;AAEA,UAAM,UAAU,qBAAqB,IAAI;AACzC,eAAW,QAAQ,SAAS;AAC1B,UAAI,CAAC,KAAK,IAAI,IAAI,EAAG,iBAAgB,MAAM,IAAI;AAAA,IACjD;AACA,eAAW,CAAC,MAAM,KAAK,KAAK,KAAM,+BAAa,MAAM,MAAM,KAAK;AAChE,yBAAqB,MAAM,KAAK,KAAK,CAAC;AAAA,EACxC,CAAC;AACH;AA/BgB;AAiCT,SAAS,cAAc,MAAe,OAAuB;AAClE,YAAU,MAAM,OAAO,MAAM,EAAE;AACjC;AAFgB;AAIT,SAAS,UAAU,MAAe,OAAiB,aAAiC;AACzF,gCAAO,MAAM;AACX,UAAM,YAAY,WAAW,OAAO,OAAO;AAC3C,UAAM,gBAAgB,YAAY;AAClC,UAAM,QAAQ,CAAC,eAAe,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAClE,QAAI,MAAO,+BAAa,MAAM,SAAS,KAAK;AAAA,QACvC,iBAAgB,MAAM,OAAO;AAAA,EACpC,CAAC;AACH;AARgB;AAqDT,SAAS,UACd,QACA,OACA,UACM;AACN,iCAAc,QAAQ,MAAM,MAAM,YAAY,QAAQ,IAAI,OAAO,QAAQ,CAAC,CAAC;AAC7E;AANgB;AAQT,SAAS,YAAY,OAAiC;AAC3D,QAAM,WAAW,OAAO,UAAU,aAAa,MAAM,IAAI;AACzD,MAAI,aAAa,UAAa,aAAa,QAAQ,aAAa,MAAO,QAAO;AAC9E,MAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAU,YAAO,wBAAW,OAAO,QAAQ,CAAC;AACpG,MAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,UAAM,WAAW;AACjB,eAAO,4BAAe,CAAC,QAAQ,WAAW;AACxC,iBAAW,SAAS,UAAU;AAC5B,cAAM,OAAO,YAAY,KAAK;AAC9B,YAAI,KAAM,+BAAa,QAAQ,MAAM,MAAM;AAAA,MAC7C;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAdgB;AA6BhB,SAAS,WAAW,OAAe,MAAkC;AACnE,QAAM,QAAQ,QAAQ,IAAI,OAAO,IAAI;AACrC,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAHS;AAKT,SAAS,kBAAkB,MAAuB;AAChD,SAAO,SAAS,QACX,SAAS,WACT,SAAS,UACT,SAAS,cACT,KAAK,WAAW,OAAO,KACvB,KAAK,WAAW,OAAO;AAC9B;AAPS;AAST,SAAS,mBAAmB,MAAc,OAA6D;AACrG,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,UAAU,SAAS,CAAC,KAAK,WAAW,OAAO,KAAK,CAAC,KAAK,WAAW,OAAO,EAAG,QAAO;AACtF,SAAO,EAAE,MAAM,SAAS,aAAa,aAAa,MAAM,OAAO,OAAO,KAAK,EAAE;AAC/E;AAJS;AAMT,IAAM,oBAAoB,oBAAI,QAA6B;AAE3D,SAAS,qBAAqB,MAA4B;AACxD,SAAO,kBAAkB,IAAI,IAAI,KAAK,oBAAI,IAAY;AACxD;AAFS;AAIT,SAAS,qBAAqB,MAAe,OAAuC;AAClF,oBAAkB,IAAI,MAAM,IAAI,IAAI,KAAK,CAAC;AAC5C;AAFS;AAIT,SAAS,gBAAgB,MAAe,MAAoB;AAC1D,QAAM,YAAY;AAClB,YAAU,kBAAkB,IAAI;AAClC;AAHS;;;ADvMF,SAAS,KAAK,QAAmB,CAAC,GAAa;AACpD,QAAM,WAAO,oCAAkB,MAAM;AACnC,UAAM,aAAa;AAAA,MACjB,SAA8C,OAAO,QAAQ,MAAS,KACjE,SAA6B,OAAO,QAAQ,MAAS;AAAA,IAC5D;AACA,QAAI,CAAC,YAAY;AACf,UAAI,QAAQ,OAAO,UAAU,EAAG,QAAO;AACvC,YAAM,IAAI,MAAM,oEAAoE;AAAA,IACtF;AACA,WAAO,oBAAoB,UAAU;AAAA,EACvC,GAAG,OAAO;AAAA,IACR,OAAO;AAAA,IACP,sBAAsB;AAAA,MACpB,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,MAClB,mBAAmB;AAAA,IACrB;AAAA,EACF,CAAC;AAED,MAAI,QAAQ,OAAO,UAAU,EAAG,WAAU,MAAM,OAAO,UAAU;AACjE,SAAO;AACT;AAxBgB;AA0BhB,SAAS,oBAAoB,YAA+C;AAC1E,SAAO;AAAA,IACL,MAAM,WAAW;AAAA,IACjB,MAAM,WAAW;AAAA,IACjB,SAAS,WAAW;AAAA,EACtB;AACF;AANS;AAQT,IAAM,iBAAiB;AAAA,EACrB,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,GAAG;AAAA,EACH,MAAM;AAAA,EACN,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,MAAM;AAAA,EACN,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,UAAU;AAAA,EACV,MAAM;AAAA,EACN,UAAU;AAAA,EACV,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,UAAU;AAAA,EACV,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,UAAU;AACZ;AAEA,IAAM,eAAe,IAAI;AAAA,EACvB,OAAO,QAAQ,cAAc,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,KAAK,CAAC,CAAC;AAC7E;AAcA,SAAS,YAAY,MAAuE;AAC1F,SAAO,OAAO,SAAS,WAAW,aAAa,IAAI,IAAI,IAAI;AAC7D;AAFS;;;AEnHT,IAAAC,qBAA2C;AAC3C,IAAAC,eAKO;AAOA,SAAS,aACd,MACA,SACA,QACU;AACV,QAAM,kBAAc,4BAAc,YAAY;AAC9C,QAAM,YAAQ,oCAAgB;AAC9B,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,iGAA0C;AACtE,UAAQ,MAAM,MAAM,MAAM;AAC1B,QAAM,UAAU,6BAAY,QAAQ,QAAQ,MAAM,MAAM,GAAxC;AAChB,QAAM,UAAU,OAAO;AACvB,SAAO;AACT;AAZgB;AAmLhB,IAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;;;AHnLH,SAAS,UAAU,QAAwB,CAAC,GAAa;AAC9D,QAAM,eAAe,MAAM,oBAAgB,qCAAgB;AAC3D,QAAM,WAAO,4BAAc,IAAI;AAC/B,gBAAc,MAAM,OAAO,MAAM;AAAA,IAC/B;AAAA,IACA,mBAAmB,SAAwB,OAAO,YAAY,WAAW,CAAC;AAAA,EAC5E,CAAC;AACD,uBAAqB,MAAM,OAAO,CAAC,gBAAgB,YAAY,cAAc,UAAU,cAAc,CAAC;AACtG,gBAAc,MAAM,KAAK;AACzB,iCAAa,MAAM,cAAc,eAAe;AAChD,iCAAa,MAAM,QAAQ,QAAQ;AAEnC,+BAAW,MAAM,MAAM,MAAM,aAAa,cAAc,OAAO,WAC7D,YAAY,OAAO,cAAc,SAAS,OAAO,cAAc,sBAAsB,CAAC,GACrF,WAAS,MAAM,EAAE;AAEpB,QAAM,SAAS,SAAgD,OAAO,UAAU,MAAS;AACzF,SAAO,SACH,aAAa,MAAM,QAAQ,SAAS,OAAO,gBAAgB,MAAS,CAAC,IACrE;AACN;AApBgB;AAsBhB,SAAS,YACP,cACA,SACA,YACU;AACV,QAAM,WAAO,4BAAc,IAAI;AAC/B,QAAM,cAAU,4BAAc,KAAK;AACnC,QAAM,YAAQ,4BAAc,QAAQ;AACpC,iCAAa,MAAM,SAAS,wBAAwB,aAAa,IAAI,EAAE;AACvE,iCAAa,MAAM,wBAAwB,aAAa,EAAE;AAC1D,iCAAa,MAAM,QAAQ,aAAa,SAAS,UAAU,UAAU,QAAQ;AAC7E,iCAAa,SAAS,SAAS,oBAAoB;AACnD,iCAAa,OAAO,SAAS,kBAAkB;AAC/C,iCAAa,OAAO,QAAQ,QAAQ;AACpC,iCAAa,OAAO,cAAc,UAAU;AAC5C,qCAAiB,OAAO,SAAS,MAAM,QAAQ,QAAQ,aAAa,EAAE,CAAC;AAEvE,MAAI,aAAa,OAAO;AACtB,UAAM,YAAQ,4BAAc,KAAK;AACjC,mCAAa,OAAO,SAAS,kBAAkB;AAC/C,mCAAa,WAAO,yBAAW,aAAa,KAAK,GAAG,IAAI;AACxD,mCAAa,SAAS,OAAO,IAAI;AAAA,EACnC;AACA,QAAM,cAAU,4BAAc,KAAK;AACnC,iCAAa,SAAS,SAAS,oBAAoB;AACnD,iCAAa,aAAS,yBAAW,aAAa,OAAO,GAAG,IAAI;AAC5D,iCAAa,SAAS,SAAS,IAAI;AACnC,iCAAa,OAAO,KAAK,EAAE,MAAM,KAAK,MAAM,IAAI,YAAY,KAAK,CAAC,GAAG,IAAI;AACzE,iCAAa,MAAM,SAAS,IAAI;AAChC,iCAAa,MAAM,OAAO,IAAI;AAC9B,SAAO;AACT;AA/BS;","names":["import_vobs","import_reactivity","import_vobs"]}
1
+ {"version":3,"sources":["../src/toast.ts","../src/icon.ts","../src/utils.ts","../src/overlay.ts"],"sourcesContent":["import { insertList } from '@vobs/vobs'\nimport {\n addEventListener,\n createElement,\n createText,\n insertBefore,\n setAttribute,\n type VobsNode\n} from '@vobs/vobs'\nimport { type Notification, type NotificationContext, useNotification } from '@vobs/notification'\nimport { Icon } from './icon'\nimport { createPortal, type VuiPortalAdapter } from './overlay'\nimport { bindClassList, bindCommonAttributes, bindUserStyle, readProp } from './utils'\nimport type { VuiCommonProps } from './types'\n\nexport type ToastPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n\nexport interface ToastHostProps extends VuiCommonProps {\n readonly notification?: NotificationContext\n readonly position?: ToastPosition\n readonly closeLabel?: string\n readonly portal?: VuiPortalAdapter<unknown>\n readonly portalTarget?: unknown\n}\n\nexport function ToastHost(props: ToastHostProps = {}): VobsNode {\n const notification = props.notification ?? useNotification()\n const root = createElement('ol')\n bindClassList(root, props, () => [\n 'vui-toast-host',\n `vui-toast-host--${readProp<ToastPosition>(props, 'position', 'top-right')}`\n ])\n bindCommonAttributes(root, props, ['notification', 'position', 'closeLabel', 'portal', 'portalTarget'])\n bindUserStyle(root, props)\n setAttribute(root, 'aria-label', 'Notifications')\n setAttribute(root, 'role', 'region')\n\n insertList(root, null, () => notification.notifications.value, entry => (\n createToast(entry, notification, readProp(props, 'closeLabel', 'Dismiss notification'))\n ), entry => entry.id)\n\n const portal = readProp<VuiPortalAdapter<unknown> | undefined>(props, 'portal', undefined)\n return portal\n ? createPortal(root, portal, readProp(props, 'portalTarget', undefined))\n : root\n}\n\nfunction createToast(\n notification: Notification,\n context: NotificationContext,\n closeLabel: string\n): VobsNode {\n const item = createElement('li')\n const content = createElement('div')\n const close = createElement('button')\n setAttribute(item, 'class', `vui-toast vui-toast--${notification.type}`)\n setAttribute(item, 'data-notification-id', notification.id)\n setAttribute(item, 'role', notification.type === 'error' ? 'alert' : 'status')\n setAttribute(content, 'class', 'vui-toast__content')\n setAttribute(close, 'class', 'vui-toast__close')\n setAttribute(close, 'type', 'button')\n setAttribute(close, 'aria-label', closeLabel)\n addEventListener(close, 'click', () => context.dismiss(notification.id))\n\n if (notification.title) {\n const title = createElement('div')\n setAttribute(title, 'class', 'vui-toast__title')\n insertBefore(title, createText(notification.title), null)\n insertBefore(content, title, null)\n }\n const message = createElement('div')\n setAttribute(message, 'class', 'vui-toast__message')\n insertBefore(message, createText(notification.content), null)\n insertBefore(content, message, null)\n insertBefore(close, Icon({ name: 'x', size: 16, decorative: true }), null)\n insertBefore(item, content, null)\n insertBefore(item, close, null)\n return item\n}\n","import {\n createIcon,\n createSvgIconNode,\n type IconDefinition,\n type SvgIconDefinition,\n type VobsIconProps\n} from '@vobs/icon-core'\nimport { state } from '@vobs/vobs'\nimport { hasProp, mountSlot, readProp } from './utils'\nimport type { VobsNode } from '@vobs/vobs'\nimport type { VuiCommonProps } from './types'\n\nexport type { IconDefinition } from '@vobs/icon-core'\n\nexport interface IconProps extends VuiCommonProps, VobsIconProps {\n readonly name?: string\n readonly icon?: IconDefinition\n}\n\nexport function Icon(props: IconProps = {}): VobsNode {\n const root = createSvgIconNode(() => {\n // 读取注册表版本号:registerIcon 后已挂载的 Icon 自动补渲染,\n // 支持异步/按需注册(如 Iconify 集合、在线加载)。\n registryVersion.value\n const definition = resolveIcon(\n readProp<IconDefinition | string | undefined>(props, 'icon', undefined)\n ?? readProp<string | undefined>(props, 'name', undefined)\n )\n if (!definition) {\n if (hasProp(props, 'children')) return undefined\n const requested = readProp<IconDefinition | string | undefined>(props, 'icon', undefined)\n ?? readProp<string | undefined>(props, 'name', undefined)\n console.warn(`Vobs UI: 未注册的图标 \"${String(requested)}\",已渲染为空。请先 registerIcon 或改用已注册的名称。`)\n return undefined\n }\n return iconDefinitionToSvg(definition)\n }, props, {\n class: 'vui-icon icon',\n defaultSvgAttributes: {\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': 2,\n 'stroke-linecap': 'butt',\n 'stroke-linejoin': 'miter'\n }\n }) as Element\n\n if (hasProp(props, 'children')) mountSlot(root, props, 'children')\n return root\n}\n\nfunction iconDefinitionToSvg(definition: IconDefinition): SvgIconDefinition {\n return {\n name: definition.name,\n body: definition.path,\n viewBox: definition.viewBox\n }\n}\n\nconst VUI_ICON_PATHS = {\n search: '<circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m20 20-3.5-3.5\"/>',\n check: '<polyline points=\"4 12 10 18 20 6\"/>',\n x: '<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>',\n plus: '<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n minus: '<line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n 'chevron-right': '<polyline points=\"9 6 15 12 9 18\"/>',\n 'chevron-down': '<polyline points=\"6 9 12 15 18 9\"/>',\n 'chevron-up': '<polyline points=\"6 15 12 9 18 15\"/>',\n 'chevron-left': '<polyline points=\"15 6 9 12 15 18\"/>',\n 'arrow-left': '<line x1=\"20\" y1=\"12\" x2=\"4\" y2=\"12\"/><polyline points=\"10 6 4 12 10 18\"/>',\n 'arrow-right': '<line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/><polyline points=\"14 6 20 12 14 18\"/>',\n 'arrow-up': '<line x1=\"12\" y1=\"20\" x2=\"12\" y2=\"4\"/><polyline points=\"6 10 12 4 18 10\"/>',\n 'arrow-down': '<line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/><polyline points=\"6 14 12 20 18 14\"/>',\n atom: '<circle cx=\"12\" cy=\"12\" r=\"2\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\" transform=\"rotate(60 12 12)\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\" transform=\"rotate(120 12 12)\"/>',\n globe: '<circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18\"/>',\n 'arrow-expand': '<polyline points=\"14 4 20 4 20 10\"/><line x1=\"14\" y1=\"10\" x2=\"20\" y2=\"4\"/><polyline points=\"10 20 4 20 4 14\"/><line x1=\"10\" y1=\"14\" x2=\"4\" y2=\"20\"/>',\n 'arrow-minimize': '<polyline points=\"20 4 14 10 20 10\"/><line x1=\"14\" y1=\"10\" x2=\"14\" y2=\"4\"/><polyline points=\"4 20 10 14 4 14\"/><line x1=\"10\" y1=\"14\" x2=\"10\" y2=\"20\"/>',\n 'check-circle': '<circle cx=\"12\" cy=\"12\" r=\"9\"/><polyline points=\"8 12 11 15 16 9\"/>',\n 'alert-circle': '<circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"16.01\"/>',\n 'alert-triangle': '<path d=\"M12 3 22 20 2 20 Z\"/><line x1=\"12\" y1=\"10\" x2=\"12\" y2=\"15\"/><line x1=\"12\" y1=\"18\" x2=\"12\" y2=\"18.01\"/>',\n info: '<circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"8.01\"/>',\n home: '<polygon points=\"3 11 12 3 21 11 21 21 14 21 14 14 10 14 10 21 3 21 3 11\"/>',\n user: '<path d=\"M4 21v-1a6 6 0 0 1 6-6h4a6 6 0 0 1 6 6v1\"/><circle cx=\"12\" cy=\"8\" r=\"4\"/>',\n settings: '<circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z\"/>',\n menu: '<line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/><line x1=\"4\" y1=\"18\" x2=\"20\" y2=\"18\"/>',\n 'more-h': '<circle cx=\"5\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"19\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/>',\n calendar: '<rect x=\"3\" y=\"5\" width=\"18\" height=\"16\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/><line x1=\"8\" y1=\"3\" x2=\"8\" y2=\"7\"/><line x1=\"16\" y1=\"3\" x2=\"16\" y2=\"7\"/>',\n filter: '<polygon points=\"3 4 21 4 14 12 14 20 10 18 10 12 3 4\"/>',\n sort: '<path d=\"M8 20V4\"/><polyline points=\"4 8 8 4 12 8\"/><path d=\"M16 4v16\"/><polyline points=\"12 16 16 20 20 16\"/>',\n edit: '<path d=\"M4 20h4l10-10-4-4L4 16v4z\"/><path d=\"M14 6l4 4\"/>',\n trash: '<polyline points=\"4 6 20 6\"/><path d=\"M6 6v14h12V6\"/><path d=\"M9 6V4h6v2\"/><line x1=\"10\" y1=\"10\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"10\" x2=\"14\" y2=\"17\"/>',\n download: '<path d=\"M12 3v12\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"21\"/>',\n upload: '<path d=\"M12 21V6\"/><polyline points=\"7 11 12 6 17 11\"/><line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"21\"/>',\n pause: '<line x1=\"9\" y1=\"5\" x2=\"9\" y2=\"19\"/><line x1=\"15\" y1=\"5\" x2=\"15\" y2=\"19\"/>',\n play: '<polygon points=\"8 5 19 12 8 19 8 5\"/>',\n refresh: '<polyline points=\"21 4 21 10 15 10\"/><polyline points=\"3 20 3 14 9 14\"/><path d=\"M20.5 9A9 9 0 0 0 5 5.5L3 7M3.5 15A9 9 0 0 0 19 18.5L21 17\"/>',\n bell: '<path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 8 3 8H3s3-1 3-8z\"/><path d=\"M10 21a2 2 0 0 0 4 0\"/>',\n folder: '<path d=\"M3 6h6l2 3h10v10H3z\"/>',\n code: '<polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/>',\n sparkles: '<path d=\"M12 3l1.8 4.2L18 9l-4.2 1.8L12 15l-1.8-4.2L6 9l4.2-1.8L12 3z\"/><path d=\"M19 14l1 2.2 2.2 1-2.2 1L19 20.4l-1-2.2-2.2-1 2.2-1L19 14z\"/>',\n zap: '<polygon points=\"13 2 4 14 12 14 11 22 20 10 12 10 13 2\"/>',\n sun: '<circle cx=\"12\" cy=\"12\" r=\"4\"/><line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"5\"/><line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"22\"/><line x1=\"2\" y1=\"12\" x2=\"5\" y2=\"12\"/><line x1=\"19\" y1=\"12\" x2=\"22\" y2=\"12\"/><line x1=\"4.6\" y1=\"4.6\" x2=\"6.7\" y2=\"6.7\"/><line x1=\"17.3\" y1=\"17.3\" x2=\"19.4\" y2=\"19.4\"/><line x1=\"4.6\" y1=\"19.4\" x2=\"6.7\" y2=\"17.3\"/><line x1=\"17.3\" y1=\"6.7\" x2=\"19.4\" y2=\"4.6\"/>',\n moon: '<path d=\"M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z\"/>',\n terminal: '<polyline points=\"4 7 9 12 4 17\"/><line x1=\"12\" y1=\"17\" x2=\"20\" y2=\"17\"/>'\n} as const\n\nconst iconRegistry = new Map<string, IconDefinition>(\n Object.entries(VUI_ICON_PATHS).map(([name, path]) => [name, { name, path }])\n)\n\n/** 注册表版本号:registerIcon/注销时递增,驱动已挂载 Icon 的 effect 重取定义。 */\nconst registryVersion = state(0, 'vui.iconRegistry.version')\n\nexport { VUI_ICON_PATHS }\nexport { createIcon, registerIcon, resolveIcon }\n\nfunction registerIcon(icon: IconDefinition): () => void {\n const previous = iconRegistry.get(icon.name)\n iconRegistry.set(icon.name, icon)\n registryVersion.value++\n return () => {\n if (previous) iconRegistry.set(icon.name, previous)\n else iconRegistry.delete(icon.name)\n registryVersion.value++\n }\n}\n\nfunction resolveIcon(icon: string | IconDefinition | undefined): IconDefinition | undefined {\n return typeof icon === 'string' ? iconRegistry.get(icon) : icon\n}\n","import { effect } from '@vobs/reactivity'\nimport {\n addEventListener,\n createFragment,\n createText,\n insertBefore,\n insertDynamic,\n setAttribute,\n setProperty,\n setTextContent,\n type VobsNode\n} from '@vobs/vobs'\nimport type { VuiCommonProps } from './types'\n\ntype VuiProps = VuiCommonProps | object\n\nexport function classNames(...values: readonly (string | false | null | undefined)[]): string {\n return values\n .flatMap(value => typeof value === 'string' ? value.trim().split(/\\s+/u) : [])\n .filter(Boolean)\n .join(' ')\n}\n\nexport function readProp<T>(props: object, name: string, fallback: T): T {\n const value = Reflect.get(props, name)\n return (value === undefined ? fallback : value) as T\n}\n\nexport function hasProp(props: object, name: string): boolean {\n return name in props\n}\n\nexport function bindClass(\n root: Element,\n props: VuiProps,\n ...baseClasses: readonly (string | undefined)[]\n): void {\n effect(() => {\n setAttribute(root, 'class', classNames(\n ...baseClasses,\n readString(props, 'class'),\n readString(props, 'className')\n ))\n })\n}\n\nexport function bindClassList(\n root: Element,\n props: VuiProps,\n readBaseClasses: () => readonly (string | undefined)[]\n): void {\n effect(() => {\n setAttribute(root, 'class', classNames(\n ...readBaseClasses(),\n readString(props, 'class'),\n readString(props, 'className')\n ))\n })\n}\n\nexport function bindCommonAttributes(\n root: Element,\n props: VuiProps,\n skip: readonly string[] = [],\n options: { readonly includeDataAria?: boolean } = {}\n): void {\n const ignored = new Set(skip)\n ignored.add('class')\n ignored.add('className')\n ignored.add('children')\n ignored.add('style')\n\n effect(() => {\n const next = new Map<string, string>()\n for (const name of Object.keys(props)) {\n if (ignored.has(name) || name.startsWith('on')) continue\n if (!isCommonAttribute(name)) continue\n if (options.includeDataAria === false && (name.startsWith('aria-') || name.startsWith('data-'))) continue\n\n const value = Reflect.get(props, name)\n const normalized = normalizeAttribute(name, value)\n if (normalized !== undefined) next.set(normalized.name, normalized.value)\n }\n\n const managed = getManagedAttributes(root)\n for (const name of managed) {\n if (!next.has(name)) removeAttribute(root, name)\n }\n for (const [name, value] of next) setAttribute(root, name, value)\n setManagedAttributes(root, next.keys())\n })\n}\n\nexport function bindUserStyle(root: Element, props: VuiProps): void {\n bindStyle(root, props, () => '')\n}\n\nexport function bindStyle(root: Element, props: VuiProps, createStyle: () => string): void {\n effect(() => {\n const userStyle = readString(props, 'style')\n const internalStyle = createStyle()\n const style = [internalStyle, userStyle].filter(Boolean).join('; ')\n if (style) setAttribute(root, 'style', style)\n else removeAttribute(root, 'style')\n })\n}\n\nexport function bindTextContent(\n node: Text,\n read: () => unknown\n): void {\n effect(() => {\n setTextContent(node, String(read() ?? ''))\n })\n}\n\nexport function bindPropertyValue(\n node: Element,\n name: string,\n read: () => unknown\n): void {\n effect(() => setProperty(node, name, read()))\n}\n\nexport function bindAttributeValue(\n node: Element,\n name: string,\n read: () => unknown\n): void {\n effect(() => {\n const value = read()\n if (value === undefined || value === null || value === false) removeAttribute(node, name)\n else setAttribute(node, name, String(value))\n })\n}\n\nexport function listen(\n root: Element,\n event: string,\n props: object,\n propName: string,\n disabled?: () => boolean\n): void {\n addEventListener(root, event, (reason: Event) => {\n if (disabled?.()) return\n const handler = Reflect.get(props, propName)\n if (typeof handler === 'function') handler(reason)\n })\n}\n\nexport function mountSlot(\n parent: Node,\n props: object,\n propName: string\n): void {\n insertDynamic(parent, null, () => resolveSlot(Reflect.get(props, propName)))\n}\n\nexport function resolveSlot(value: unknown): VobsNode | null {\n const resolved = typeof value === 'function' ? value() : value\n if (resolved === undefined || resolved === null || resolved === false) return null\n if (typeof resolved === 'string' || typeof resolved === 'number') return createText(String(resolved))\n if (Array.isArray(resolved)) {\n const children = resolved\n return createFragment((parent, anchor) => {\n for (const child of children) {\n const node = resolveSlot(child)\n if (node) insertBefore(parent, node, anchor)\n }\n })\n }\n return resolved as VobsNode\n}\n\nexport function setOptionalAttribute(node: Element, name: string, value: unknown): void {\n if (value === undefined || value === null || value === false || value === '') {\n removeAttribute(node, name)\n return\n }\n setAttribute(node, name, String(value))\n}\n\nexport function setOptionalProperty(node: Element, name: string, value: unknown): void {\n if (value === undefined || value === null) return\n setProperty(node, name, value)\n}\n\nfunction readString(props: object, name: string): string | undefined {\n const value = Reflect.get(props, name)\n return typeof value === 'string' ? value : undefined\n}\n\nfunction isCommonAttribute(name: string): boolean {\n return name === 'id'\n || name === 'title'\n || name === 'role'\n || name === 'tabIndex'\n || name.startsWith('aria-')\n || name.startsWith('data-')\n}\n\nfunction normalizeAttribute(name: string, value: unknown): { name: string; value: string } | undefined {\n if (value === undefined || value === null) return undefined\n if (value === false && !name.startsWith('aria-') && !name.startsWith('data-')) return undefined\n return { name: name === 'tabIndex' ? 'tabindex' : name, value: String(value) }\n}\n\nconst managedAttributes = new WeakMap<object, Set<string>>()\n\nfunction getManagedAttributes(node: Element): Set<string> {\n return managedAttributes.get(node) ?? new Set<string>()\n}\n\nfunction setManagedAttributes(node: Element, names: IterableIterator<string>): void {\n managedAttributes.set(node, new Set(names))\n}\n\nfunction removeAttribute(node: Element, name: string): void {\n const candidate = node as Element & { removeAttribute?: (attribute: string) => void }\n candidate.removeAttribute?.(name)\n}\n","import { getCurrentOwner, onDispose } from '@vobs/reactivity'\nimport {\n createComment,\n insertBefore,\n isVobsFragment,\n type VobsNode\n} from '@vobs/vobs'\n\nexport interface VuiPortalAdapter<Target = unknown> {\n mount(node: VobsNode, target: Target): void\n unmount(node: VobsNode, target: Target): void\n}\n\nexport function createPortal<Target>(\n node: VobsNode,\n adapter: VuiPortalAdapter<Target>,\n target: Target\n): VobsNode {\n const placeholder = createComment('vui:portal')\n const owner = getCurrentOwner()\n if (!owner) throw new Error('Vobs UI: createPortal 必须在组件 Owner 作用域内调用')\n adapter.mount(node, target)\n const cleanup = (): void => adapter.unmount(node, target)\n owner.onDispose(cleanup)\n return placeholder\n}\n\nexport function createDOMPortalAdapter(): VuiPortalAdapter<Node> {\n return {\n mount(node, target): void {\n insertBefore(target, node, null)\n },\n unmount(node, target): void {\n removePortalNode(node, target)\n }\n }\n}\n\nexport interface FocusTrapOptions {\n readonly initialFocus?: HTMLElement | (() => HTMLElement | null)\n readonly restoreFocus?: boolean\n readonly onEscape?: (event: KeyboardEvent) => void\n}\n\nexport interface FocusTrapHandle {\n readonly active: boolean\n activate(): void\n deactivate(): void\n dispose(): void\n}\n\nexport function createFocusTrap(\n root: HTMLElement,\n options: FocusTrapOptions = {}\n): FocusTrapHandle {\n let isActive = false\n let disposed = false\n let previouslyFocused: Element | null = null\n\n const focusable = (): HTMLElement[] => [...root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)]\n .filter(element => !element.hasAttribute('disabled') && element.getAttribute('aria-hidden') !== 'true')\n\n const onKeyDown = (event: KeyboardEvent): void => {\n if (!isActive || disposed) return\n if (event.key === 'Escape') {\n options.onEscape?.(event)\n return\n }\n if (event.key !== 'Tab') return\n const elements = focusable()\n if (elements.length === 0) {\n event.preventDefault()\n root.focus()\n return\n }\n const first = elements[0]\n const last = elements[elements.length - 1]\n const current = document.activeElement\n if (event.shiftKey && current === first) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && current === last) {\n event.preventDefault()\n first.focus()\n }\n }\n\n const activate = (): void => {\n if (disposed || isActive) return\n isActive = true\n previouslyFocused = document.activeElement\n root.addEventListener('keydown', onKeyDown)\n const initial = resolveInitialFocus(options.initialFocus) ?? focusable()[0] ?? root\n if (!root.hasAttribute('tabindex') && initial === root) root.setAttribute('tabindex', '-1')\n initial.focus()\n }\n\n const deactivate = (): void => {\n if (!isActive) return\n isActive = false\n root.removeEventListener('keydown', onKeyDown)\n if (options.restoreFocus !== false\n && typeof HTMLElement !== 'undefined'\n && previouslyFocused instanceof HTMLElement) previouslyFocused.focus()\n previouslyFocused = null\n }\n\n const handle: FocusTrapHandle = {\n get active(): boolean { return isActive },\n activate,\n deactivate,\n dispose(): void {\n if (disposed) return\n deactivate()\n disposed = true\n }\n }\n if (getCurrentOwner()) onDispose(() => handle.dispose())\n return handle\n}\n\nexport interface OverlayEntry {\n readonly id: string\n readonly modal?: boolean\n readonly closeOnEscape?: boolean\n readonly onEscape?: () => void\n}\n\nexport interface OverlayManager {\n readonly entries: readonly OverlayEntry[]\n open(entry: OverlayEntry): void\n close(id: string): void\n bringToFront(id: string): void\n subscribe(listener: (entries: readonly OverlayEntry[]) => void): () => void\n}\n\nexport function createOverlayManager(): OverlayManager {\n let entries: OverlayEntry[] = []\n const listeners = new Set<(entries: readonly OverlayEntry[]) => void>()\n const notify = (): void => {\n const snapshot = entries.slice()\n for (const listener of listeners) listener(snapshot)\n }\n\n return {\n get entries(): readonly OverlayEntry[] { return entries },\n open(entry): void {\n entries = [...entries.filter(current => current.id !== entry.id), entry]\n notify()\n },\n close(id): void {\n const next = entries.filter(entry => entry.id !== id)\n if (next.length === entries.length) return\n entries = next\n notify()\n },\n bringToFront(id): void {\n const entry = entries.find(current => current.id === id)\n if (!entry || entries[entries.length - 1] === entry) return\n entries = [...entries.filter(current => current !== entry), entry]\n notify()\n },\n subscribe(listener): () => void {\n listeners.add(listener)\n listener(entries.slice())\n return () => listeners.delete(listener)\n }\n }\n}\n\nfunction resolveInitialFocus(\n target: FocusTrapOptions['initialFocus']\n): HTMLElement | null {\n if (!target) return null\n return typeof target === 'function' ? target() : target\n}\n\nfunction removePortalNode(node: VobsNode, target: Node): void {\n if (isVobsFragment(node)) {\n let current = node.start.nextSibling\n while (current && current !== node.end) {\n const next = current.nextSibling\n target.removeChild(current)\n current = next\n }\n if (node.start.parentNode === target) target.removeChild(node.start)\n if (node.end.parentNode === target) target.removeChild(node.end)\n return\n }\n if (node.parentNode === target) target.removeChild(node)\n}\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button',\n 'input',\n 'select',\n 'textarea',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]'\n].join(',')\n"],"mappings":"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","names":["import_vobs","import_vobs","import_reactivity","import_vobs"]}
package/dist/toast.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  createIcon,
18
18
  createSvgIconNode
19
19
  } from "@vobs/icon-core";
20
+ import { state } from "@vobs/vobs";
20
21
 
21
22
  // packages/ui/src/utils.ts
22
23
  import { effect } from "@vobs/reactivity";
@@ -145,12 +146,15 @@ __name(removeAttribute, "removeAttribute");
145
146
  // packages/ui/src/icon.ts
146
147
  function Icon(props = {}) {
147
148
  const root = createSvgIconNode(() => {
149
+ registryVersion.value;
148
150
  const definition = resolveIcon(
149
151
  readProp(props, "icon", void 0) ?? readProp(props, "name", void 0)
150
152
  );
151
153
  if (!definition) {
152
154
  if (hasProp(props, "children")) return void 0;
153
- throw new Error("Vobs UI: Icon requires a registered `name` or an `icon` definition");
155
+ const requested = readProp(props, "icon", void 0) ?? readProp(props, "name", void 0);
156
+ console.warn(`Vobs UI: \u672A\u6CE8\u518C\u7684\u56FE\u6807 "${String(requested)}"\uFF0C\u5DF2\u6E32\u67D3\u4E3A\u7A7A\u3002\u8BF7\u5148 registerIcon \u6216\u6539\u7528\u5DF2\u6CE8\u518C\u7684\u540D\u79F0\u3002`);
157
+ return void 0;
154
158
  }
155
159
  return iconDefinitionToSvg(definition);
156
160
  }, props, {
@@ -224,6 +228,7 @@ var VUI_ICON_PATHS = {
224
228
  var iconRegistry = new Map(
225
229
  Object.entries(VUI_ICON_PATHS).map(([name, path]) => [name, { name, path }])
226
230
  );
231
+ var registryVersion = state(0, "vui.iconRegistry.version");
227
232
  function resolveIcon(icon) {
228
233
  return typeof icon === "string" ? iconRegistry.get(icon) : icon;
229
234
  }
package/dist/toast.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/toast.ts","../src/icon.ts","../src/utils.ts","../src/overlay.ts"],"sourcesContent":["import { insertList } from '@vobs/vobs'\nimport {\n addEventListener,\n createElement,\n createText,\n insertBefore,\n setAttribute,\n type VobsNode\n} from '@vobs/vobs'\nimport { type Notification, type NotificationContext, useNotification } from '@vobs/notification'\nimport { Icon } from './icon'\nimport { createPortal, type VuiPortalAdapter } from './overlay'\nimport { bindClassList, bindCommonAttributes, bindUserStyle, readProp } from './utils'\nimport type { VuiCommonProps } from './types'\n\nexport type ToastPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n\nexport interface ToastHostProps extends VuiCommonProps {\n readonly notification?: NotificationContext\n readonly position?: ToastPosition\n readonly closeLabel?: string\n readonly portal?: VuiPortalAdapter<unknown>\n readonly portalTarget?: unknown\n}\n\nexport function ToastHost(props: ToastHostProps = {}): VobsNode {\n const notification = props.notification ?? useNotification()\n const root = createElement('ol')\n bindClassList(root, props, () => [\n 'vui-toast-host',\n `vui-toast-host--${readProp<ToastPosition>(props, 'position', 'top-right')}`\n ])\n bindCommonAttributes(root, props, ['notification', 'position', 'closeLabel', 'portal', 'portalTarget'])\n bindUserStyle(root, props)\n setAttribute(root, 'aria-label', 'Notifications')\n setAttribute(root, 'role', 'region')\n\n insertList(root, null, () => notification.notifications.value, entry => (\n createToast(entry, notification, readProp(props, 'closeLabel', 'Dismiss notification'))\n ), entry => entry.id)\n\n const portal = readProp<VuiPortalAdapter<unknown> | undefined>(props, 'portal', undefined)\n return portal\n ? createPortal(root, portal, readProp(props, 'portalTarget', undefined))\n : root\n}\n\nfunction createToast(\n notification: Notification,\n context: NotificationContext,\n closeLabel: string\n): VobsNode {\n const item = createElement('li')\n const content = createElement('div')\n const close = createElement('button')\n setAttribute(item, 'class', `vui-toast vui-toast--${notification.type}`)\n setAttribute(item, 'data-notification-id', notification.id)\n setAttribute(item, 'role', notification.type === 'error' ? 'alert' : 'status')\n setAttribute(content, 'class', 'vui-toast__content')\n setAttribute(close, 'class', 'vui-toast__close')\n setAttribute(close, 'type', 'button')\n setAttribute(close, 'aria-label', closeLabel)\n addEventListener(close, 'click', () => context.dismiss(notification.id))\n\n if (notification.title) {\n const title = createElement('div')\n setAttribute(title, 'class', 'vui-toast__title')\n insertBefore(title, createText(notification.title), null)\n insertBefore(content, title, null)\n }\n const message = createElement('div')\n setAttribute(message, 'class', 'vui-toast__message')\n insertBefore(message, createText(notification.content), null)\n insertBefore(content, message, null)\n insertBefore(close, Icon({ name: 'x', size: 16, decorative: true }), null)\n insertBefore(item, content, null)\n insertBefore(item, close, null)\n return item\n}\n","import {\n createIcon,\n createSvgIconNode,\n type IconDefinition,\n type SvgIconDefinition,\n type VobsIconProps\n} from '@vobs/icon-core'\nimport { hasProp, mountSlot, readProp } from './utils'\nimport type { VobsNode } from '@vobs/vobs'\nimport type { VuiCommonProps } from './types'\n\nexport type { IconDefinition } from '@vobs/icon-core'\n\nexport interface IconProps extends VuiCommonProps, VobsIconProps {\n readonly name?: string\n readonly icon?: IconDefinition\n}\n\nexport function Icon(props: IconProps = {}): VobsNode {\n const root = createSvgIconNode(() => {\n const definition = resolveIcon(\n readProp<IconDefinition | string | undefined>(props, 'icon', undefined)\n ?? readProp<string | undefined>(props, 'name', undefined)\n )\n if (!definition) {\n if (hasProp(props, 'children')) return undefined\n throw new Error('Vobs UI: Icon requires a registered `name` or an `icon` definition')\n }\n return iconDefinitionToSvg(definition)\n }, props, {\n class: 'vui-icon icon',\n defaultSvgAttributes: {\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': 2,\n 'stroke-linecap': 'butt',\n 'stroke-linejoin': 'miter'\n }\n }) as Element\n\n if (hasProp(props, 'children')) mountSlot(root, props, 'children')\n return root\n}\n\nfunction iconDefinitionToSvg(definition: IconDefinition): SvgIconDefinition {\n return {\n name: definition.name,\n body: definition.path,\n viewBox: definition.viewBox\n }\n}\n\nconst VUI_ICON_PATHS = {\n search: '<circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m20 20-3.5-3.5\"/>',\n check: '<polyline points=\"4 12 10 18 20 6\"/>',\n x: '<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>',\n plus: '<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n minus: '<line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n 'chevron-right': '<polyline points=\"9 6 15 12 9 18\"/>',\n 'chevron-down': '<polyline points=\"6 9 12 15 18 9\"/>',\n 'chevron-up': '<polyline points=\"6 15 12 9 18 15\"/>',\n 'chevron-left': '<polyline points=\"15 6 9 12 15 18\"/>',\n 'arrow-left': '<line x1=\"20\" y1=\"12\" x2=\"4\" y2=\"12\"/><polyline points=\"10 6 4 12 10 18\"/>',\n 'arrow-right': '<line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/><polyline points=\"14 6 20 12 14 18\"/>',\n 'arrow-up': '<line x1=\"12\" y1=\"20\" x2=\"12\" y2=\"4\"/><polyline points=\"6 10 12 4 18 10\"/>',\n 'arrow-down': '<line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/><polyline points=\"6 14 12 20 18 14\"/>',\n atom: '<circle cx=\"12\" cy=\"12\" r=\"2\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\" transform=\"rotate(60 12 12)\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\" transform=\"rotate(120 12 12)\"/>',\n globe: '<circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18\"/>',\n 'arrow-expand': '<polyline points=\"14 4 20 4 20 10\"/><line x1=\"14\" y1=\"10\" x2=\"20\" y2=\"4\"/><polyline points=\"10 20 4 20 4 14\"/><line x1=\"10\" y1=\"14\" x2=\"4\" y2=\"20\"/>',\n 'arrow-minimize': '<polyline points=\"20 4 14 10 20 10\"/><line x1=\"14\" y1=\"10\" x2=\"14\" y2=\"4\"/><polyline points=\"4 20 10 14 4 14\"/><line x1=\"10\" y1=\"14\" x2=\"10\" y2=\"20\"/>',\n 'check-circle': '<circle cx=\"12\" cy=\"12\" r=\"9\"/><polyline points=\"8 12 11 15 16 9\"/>',\n 'alert-circle': '<circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"16.01\"/>',\n 'alert-triangle': '<path d=\"M12 3 22 20 2 20 Z\"/><line x1=\"12\" y1=\"10\" x2=\"12\" y2=\"15\"/><line x1=\"12\" y1=\"18\" x2=\"12\" y2=\"18.01\"/>',\n info: '<circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"8.01\"/>',\n home: '<polygon points=\"3 11 12 3 21 11 21 21 14 21 14 14 10 14 10 21 3 21 3 11\"/>',\n user: '<path d=\"M4 21v-1a6 6 0 0 1 6-6h4a6 6 0 0 1 6 6v1\"/><circle cx=\"12\" cy=\"8\" r=\"4\"/>',\n settings: '<circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z\"/>',\n menu: '<line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/><line x1=\"4\" y1=\"18\" x2=\"20\" y2=\"18\"/>',\n 'more-h': '<circle cx=\"5\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"19\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/>',\n calendar: '<rect x=\"3\" y=\"5\" width=\"18\" height=\"16\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/><line x1=\"8\" y1=\"3\" x2=\"8\" y2=\"7\"/><line x1=\"16\" y1=\"3\" x2=\"16\" y2=\"7\"/>',\n filter: '<polygon points=\"3 4 21 4 14 12 14 20 10 18 10 12 3 4\"/>',\n sort: '<path d=\"M8 20V4\"/><polyline points=\"4 8 8 4 12 8\"/><path d=\"M16 4v16\"/><polyline points=\"12 16 16 20 20 16\"/>',\n edit: '<path d=\"M4 20h4l10-10-4-4L4 16v4z\"/><path d=\"M14 6l4 4\"/>',\n trash: '<polyline points=\"4 6 20 6\"/><path d=\"M6 6v14h12V6\"/><path d=\"M9 6V4h6v2\"/><line x1=\"10\" y1=\"10\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"10\" x2=\"14\" y2=\"17\"/>',\n download: '<path d=\"M12 3v12\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"21\"/>',\n upload: '<path d=\"M12 21V6\"/><polyline points=\"7 11 12 6 17 11\"/><line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"21\"/>',\n pause: '<line x1=\"9\" y1=\"5\" x2=\"9\" y2=\"19\"/><line x1=\"15\" y1=\"5\" x2=\"15\" y2=\"19\"/>',\n play: '<polygon points=\"8 5 19 12 8 19 8 5\"/>',\n refresh: '<polyline points=\"21 4 21 10 15 10\"/><polyline points=\"3 20 3 14 9 14\"/><path d=\"M20.5 9A9 9 0 0 0 5 5.5L3 7M3.5 15A9 9 0 0 0 19 18.5L21 17\"/>',\n bell: '<path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 8 3 8H3s3-1 3-8z\"/><path d=\"M10 21a2 2 0 0 0 4 0\"/>',\n folder: '<path d=\"M3 6h6l2 3h10v10H3z\"/>',\n code: '<polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/>',\n sparkles: '<path d=\"M12 3l1.8 4.2L18 9l-4.2 1.8L12 15l-1.8-4.2L6 9l4.2-1.8L12 3z\"/><path d=\"M19 14l1 2.2 2.2 1-2.2 1L19 20.4l-1-2.2-2.2-1 2.2-1L19 14z\"/>',\n zap: '<polygon points=\"13 2 4 14 12 14 11 22 20 10 12 10 13 2\"/>',\n sun: '<circle cx=\"12\" cy=\"12\" r=\"4\"/><line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"5\"/><line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"22\"/><line x1=\"2\" y1=\"12\" x2=\"5\" y2=\"12\"/><line x1=\"19\" y1=\"12\" x2=\"22\" y2=\"12\"/><line x1=\"4.6\" y1=\"4.6\" x2=\"6.7\" y2=\"6.7\"/><line x1=\"17.3\" y1=\"17.3\" x2=\"19.4\" y2=\"19.4\"/><line x1=\"4.6\" y1=\"19.4\" x2=\"6.7\" y2=\"17.3\"/><line x1=\"17.3\" y1=\"6.7\" x2=\"19.4\" y2=\"4.6\"/>',\n moon: '<path d=\"M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z\"/>',\n terminal: '<polyline points=\"4 7 9 12 4 17\"/><line x1=\"12\" y1=\"17\" x2=\"20\" y2=\"17\"/>'\n} as const\n\nconst iconRegistry = new Map<string, IconDefinition>(\n Object.entries(VUI_ICON_PATHS).map(([name, path]) => [name, { name, path }])\n)\n\nexport { VUI_ICON_PATHS }\nexport { createIcon, registerIcon, resolveIcon }\n\nfunction registerIcon(icon: IconDefinition): () => void {\n const previous = iconRegistry.get(icon.name)\n iconRegistry.set(icon.name, icon)\n return () => {\n if (previous) iconRegistry.set(icon.name, previous)\n else iconRegistry.delete(icon.name)\n }\n}\n\nfunction resolveIcon(icon: string | IconDefinition | undefined): IconDefinition | undefined {\n return typeof icon === 'string' ? iconRegistry.get(icon) : icon\n}\n","import { effect } from '@vobs/reactivity'\nimport {\n addEventListener,\n createFragment,\n createText,\n insertBefore,\n insertDynamic,\n setAttribute,\n setProperty,\n setTextContent,\n type VobsNode\n} from '@vobs/vobs'\nimport type { VuiCommonProps } from './types'\n\ntype VuiProps = VuiCommonProps | object\n\nexport function classNames(...values: readonly (string | false | null | undefined)[]): string {\n return values\n .flatMap(value => typeof value === 'string' ? value.trim().split(/\\s+/u) : [])\n .filter(Boolean)\n .join(' ')\n}\n\nexport function readProp<T>(props: object, name: string, fallback: T): T {\n const value = Reflect.get(props, name)\n return (value === undefined ? fallback : value) as T\n}\n\nexport function hasProp(props: object, name: string): boolean {\n return name in props\n}\n\nexport function bindClass(\n root: Element,\n props: VuiProps,\n ...baseClasses: readonly (string | undefined)[]\n): void {\n effect(() => {\n setAttribute(root, 'class', classNames(\n ...baseClasses,\n readString(props, 'class'),\n readString(props, 'className')\n ))\n })\n}\n\nexport function bindClassList(\n root: Element,\n props: VuiProps,\n readBaseClasses: () => readonly (string | undefined)[]\n): void {\n effect(() => {\n setAttribute(root, 'class', classNames(\n ...readBaseClasses(),\n readString(props, 'class'),\n readString(props, 'className')\n ))\n })\n}\n\nexport function bindCommonAttributes(\n root: Element,\n props: VuiProps,\n skip: readonly string[] = [],\n options: { readonly includeDataAria?: boolean } = {}\n): void {\n const ignored = new Set(skip)\n ignored.add('class')\n ignored.add('className')\n ignored.add('children')\n ignored.add('style')\n\n effect(() => {\n const next = new Map<string, string>()\n for (const name of Object.keys(props)) {\n if (ignored.has(name) || name.startsWith('on')) continue\n if (!isCommonAttribute(name)) continue\n if (options.includeDataAria === false && (name.startsWith('aria-') || name.startsWith('data-'))) continue\n\n const value = Reflect.get(props, name)\n const normalized = normalizeAttribute(name, value)\n if (normalized !== undefined) next.set(normalized.name, normalized.value)\n }\n\n const managed = getManagedAttributes(root)\n for (const name of managed) {\n if (!next.has(name)) removeAttribute(root, name)\n }\n for (const [name, value] of next) setAttribute(root, name, value)\n setManagedAttributes(root, next.keys())\n })\n}\n\nexport function bindUserStyle(root: Element, props: VuiProps): void {\n bindStyle(root, props, () => '')\n}\n\nexport function bindStyle(root: Element, props: VuiProps, createStyle: () => string): void {\n effect(() => {\n const userStyle = readString(props, 'style')\n const internalStyle = createStyle()\n const style = [internalStyle, userStyle].filter(Boolean).join('; ')\n if (style) setAttribute(root, 'style', style)\n else removeAttribute(root, 'style')\n })\n}\n\nexport function bindTextContent(\n node: Text,\n read: () => unknown\n): void {\n effect(() => {\n setTextContent(node, String(read() ?? ''))\n })\n}\n\nexport function bindPropertyValue(\n node: Element,\n name: string,\n read: () => unknown\n): void {\n effect(() => setProperty(node, name, read()))\n}\n\nexport function bindAttributeValue(\n node: Element,\n name: string,\n read: () => unknown\n): void {\n effect(() => {\n const value = read()\n if (value === undefined || value === null || value === false) removeAttribute(node, name)\n else setAttribute(node, name, String(value))\n })\n}\n\nexport function listen(\n root: Element,\n event: string,\n props: object,\n propName: string,\n disabled?: () => boolean\n): void {\n addEventListener(root, event, (reason: Event) => {\n if (disabled?.()) return\n const handler = Reflect.get(props, propName)\n if (typeof handler === 'function') handler(reason)\n })\n}\n\nexport function mountSlot(\n parent: Node,\n props: object,\n propName: string\n): void {\n insertDynamic(parent, null, () => resolveSlot(Reflect.get(props, propName)))\n}\n\nexport function resolveSlot(value: unknown): VobsNode | null {\n const resolved = typeof value === 'function' ? value() : value\n if (resolved === undefined || resolved === null || resolved === false) return null\n if (typeof resolved === 'string' || typeof resolved === 'number') return createText(String(resolved))\n if (Array.isArray(resolved)) {\n const children = resolved\n return createFragment((parent, anchor) => {\n for (const child of children) {\n const node = resolveSlot(child)\n if (node) insertBefore(parent, node, anchor)\n }\n })\n }\n return resolved as VobsNode\n}\n\nexport function setOptionalAttribute(node: Element, name: string, value: unknown): void {\n if (value === undefined || value === null || value === false || value === '') {\n removeAttribute(node, name)\n return\n }\n setAttribute(node, name, String(value))\n}\n\nexport function setOptionalProperty(node: Element, name: string, value: unknown): void {\n if (value === undefined || value === null) return\n setProperty(node, name, value)\n}\n\nfunction readString(props: object, name: string): string | undefined {\n const value = Reflect.get(props, name)\n return typeof value === 'string' ? value : undefined\n}\n\nfunction isCommonAttribute(name: string): boolean {\n return name === 'id'\n || name === 'title'\n || name === 'role'\n || name === 'tabIndex'\n || name.startsWith('aria-')\n || name.startsWith('data-')\n}\n\nfunction normalizeAttribute(name: string, value: unknown): { name: string; value: string } | undefined {\n if (value === undefined || value === null) return undefined\n if (value === false && !name.startsWith('aria-') && !name.startsWith('data-')) return undefined\n return { name: name === 'tabIndex' ? 'tabindex' : name, value: String(value) }\n}\n\nconst managedAttributes = new WeakMap<object, Set<string>>()\n\nfunction getManagedAttributes(node: Element): Set<string> {\n return managedAttributes.get(node) ?? new Set<string>()\n}\n\nfunction setManagedAttributes(node: Element, names: IterableIterator<string>): void {\n managedAttributes.set(node, new Set(names))\n}\n\nfunction removeAttribute(node: Element, name: string): void {\n const candidate = node as Element & { removeAttribute?: (attribute: string) => void }\n candidate.removeAttribute?.(name)\n}\n","import { getCurrentOwner, onDispose } from '@vobs/reactivity'\nimport {\n createComment,\n insertBefore,\n isVobsFragment,\n type VobsNode\n} from '@vobs/vobs'\n\nexport interface VuiPortalAdapter<Target = unknown> {\n mount(node: VobsNode, target: Target): void\n unmount(node: VobsNode, target: Target): void\n}\n\nexport function createPortal<Target>(\n node: VobsNode,\n adapter: VuiPortalAdapter<Target>,\n target: Target\n): VobsNode {\n const placeholder = createComment('vui:portal')\n const owner = getCurrentOwner()\n if (!owner) throw new Error('Vobs UI: createPortal 必须在组件 Owner 作用域内调用')\n adapter.mount(node, target)\n const cleanup = (): void => adapter.unmount(node, target)\n owner.onDispose(cleanup)\n return placeholder\n}\n\nexport function createDOMPortalAdapter(): VuiPortalAdapter<Node> {\n return {\n mount(node, target): void {\n insertBefore(target, node, null)\n },\n unmount(node, target): void {\n removePortalNode(node, target)\n }\n }\n}\n\nexport interface FocusTrapOptions {\n readonly initialFocus?: HTMLElement | (() => HTMLElement | null)\n readonly restoreFocus?: boolean\n readonly onEscape?: (event: KeyboardEvent) => void\n}\n\nexport interface FocusTrapHandle {\n readonly active: boolean\n activate(): void\n deactivate(): void\n dispose(): void\n}\n\nexport function createFocusTrap(\n root: HTMLElement,\n options: FocusTrapOptions = {}\n): FocusTrapHandle {\n let isActive = false\n let disposed = false\n let previouslyFocused: Element | null = null\n\n const focusable = (): HTMLElement[] => [...root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)]\n .filter(element => !element.hasAttribute('disabled') && element.getAttribute('aria-hidden') !== 'true')\n\n const onKeyDown = (event: KeyboardEvent): void => {\n if (!isActive || disposed) return\n if (event.key === 'Escape') {\n options.onEscape?.(event)\n return\n }\n if (event.key !== 'Tab') return\n const elements = focusable()\n if (elements.length === 0) {\n event.preventDefault()\n root.focus()\n return\n }\n const first = elements[0]\n const last = elements[elements.length - 1]\n const current = document.activeElement\n if (event.shiftKey && current === first) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && current === last) {\n event.preventDefault()\n first.focus()\n }\n }\n\n const activate = (): void => {\n if (disposed || isActive) return\n isActive = true\n previouslyFocused = document.activeElement\n root.addEventListener('keydown', onKeyDown)\n const initial = resolveInitialFocus(options.initialFocus) ?? focusable()[0] ?? root\n if (!root.hasAttribute('tabindex') && initial === root) root.setAttribute('tabindex', '-1')\n initial.focus()\n }\n\n const deactivate = (): void => {\n if (!isActive) return\n isActive = false\n root.removeEventListener('keydown', onKeyDown)\n if (options.restoreFocus !== false\n && typeof HTMLElement !== 'undefined'\n && previouslyFocused instanceof HTMLElement) previouslyFocused.focus()\n previouslyFocused = null\n }\n\n const handle: FocusTrapHandle = {\n get active(): boolean { return isActive },\n activate,\n deactivate,\n dispose(): void {\n if (disposed) return\n deactivate()\n disposed = true\n }\n }\n if (getCurrentOwner()) onDispose(() => handle.dispose())\n return handle\n}\n\nexport interface OverlayEntry {\n readonly id: string\n readonly modal?: boolean\n readonly closeOnEscape?: boolean\n readonly onEscape?: () => void\n}\n\nexport interface OverlayManager {\n readonly entries: readonly OverlayEntry[]\n open(entry: OverlayEntry): void\n close(id: string): void\n bringToFront(id: string): void\n subscribe(listener: (entries: readonly OverlayEntry[]) => void): () => void\n}\n\nexport function createOverlayManager(): OverlayManager {\n let entries: OverlayEntry[] = []\n const listeners = new Set<(entries: readonly OverlayEntry[]) => void>()\n const notify = (): void => {\n const snapshot = entries.slice()\n for (const listener of listeners) listener(snapshot)\n }\n\n return {\n get entries(): readonly OverlayEntry[] { return entries },\n open(entry): void {\n entries = [...entries.filter(current => current.id !== entry.id), entry]\n notify()\n },\n close(id): void {\n const next = entries.filter(entry => entry.id !== id)\n if (next.length === entries.length) return\n entries = next\n notify()\n },\n bringToFront(id): void {\n const entry = entries.find(current => current.id === id)\n if (!entry || entries[entries.length - 1] === entry) return\n entries = [...entries.filter(current => current !== entry), entry]\n notify()\n },\n subscribe(listener): () => void {\n listeners.add(listener)\n listener(entries.slice())\n return () => listeners.delete(listener)\n }\n }\n}\n\nfunction resolveInitialFocus(\n target: FocusTrapOptions['initialFocus']\n): HTMLElement | null {\n if (!target) return null\n return typeof target === 'function' ? target() : target\n}\n\nfunction removePortalNode(node: VobsNode, target: Node): void {\n if (isVobsFragment(node)) {\n let current = node.start.nextSibling\n while (current && current !== node.end) {\n const next = current.nextSibling\n target.removeChild(current)\n current = next\n }\n if (node.start.parentNode === target) target.removeChild(node.start)\n if (node.end.parentNode === target) target.removeChild(node.end)\n return\n }\n if (node.parentNode === target) target.removeChild(node)\n}\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button',\n 'input',\n 'select',\n 'textarea',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]'\n].join(',')\n"],"mappings":"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","names":["addEventListener","createText","insertBefore","setAttribute","insertBefore","setAttribute","addEventListener","insertBefore","createText"]}
1
+ {"version":3,"sources":["../src/toast.ts","../src/icon.ts","../src/utils.ts","../src/overlay.ts"],"sourcesContent":["import { insertList } from '@vobs/vobs'\nimport {\n addEventListener,\n createElement,\n createText,\n insertBefore,\n setAttribute,\n type VobsNode\n} from '@vobs/vobs'\nimport { type Notification, type NotificationContext, useNotification } from '@vobs/notification'\nimport { Icon } from './icon'\nimport { createPortal, type VuiPortalAdapter } from './overlay'\nimport { bindClassList, bindCommonAttributes, bindUserStyle, readProp } from './utils'\nimport type { VuiCommonProps } from './types'\n\nexport type ToastPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n\nexport interface ToastHostProps extends VuiCommonProps {\n readonly notification?: NotificationContext\n readonly position?: ToastPosition\n readonly closeLabel?: string\n readonly portal?: VuiPortalAdapter<unknown>\n readonly portalTarget?: unknown\n}\n\nexport function ToastHost(props: ToastHostProps = {}): VobsNode {\n const notification = props.notification ?? useNotification()\n const root = createElement('ol')\n bindClassList(root, props, () => [\n 'vui-toast-host',\n `vui-toast-host--${readProp<ToastPosition>(props, 'position', 'top-right')}`\n ])\n bindCommonAttributes(root, props, ['notification', 'position', 'closeLabel', 'portal', 'portalTarget'])\n bindUserStyle(root, props)\n setAttribute(root, 'aria-label', 'Notifications')\n setAttribute(root, 'role', 'region')\n\n insertList(root, null, () => notification.notifications.value, entry => (\n createToast(entry, notification, readProp(props, 'closeLabel', 'Dismiss notification'))\n ), entry => entry.id)\n\n const portal = readProp<VuiPortalAdapter<unknown> | undefined>(props, 'portal', undefined)\n return portal\n ? createPortal(root, portal, readProp(props, 'portalTarget', undefined))\n : root\n}\n\nfunction createToast(\n notification: Notification,\n context: NotificationContext,\n closeLabel: string\n): VobsNode {\n const item = createElement('li')\n const content = createElement('div')\n const close = createElement('button')\n setAttribute(item, 'class', `vui-toast vui-toast--${notification.type}`)\n setAttribute(item, 'data-notification-id', notification.id)\n setAttribute(item, 'role', notification.type === 'error' ? 'alert' : 'status')\n setAttribute(content, 'class', 'vui-toast__content')\n setAttribute(close, 'class', 'vui-toast__close')\n setAttribute(close, 'type', 'button')\n setAttribute(close, 'aria-label', closeLabel)\n addEventListener(close, 'click', () => context.dismiss(notification.id))\n\n if (notification.title) {\n const title = createElement('div')\n setAttribute(title, 'class', 'vui-toast__title')\n insertBefore(title, createText(notification.title), null)\n insertBefore(content, title, null)\n }\n const message = createElement('div')\n setAttribute(message, 'class', 'vui-toast__message')\n insertBefore(message, createText(notification.content), null)\n insertBefore(content, message, null)\n insertBefore(close, Icon({ name: 'x', size: 16, decorative: true }), null)\n insertBefore(item, content, null)\n insertBefore(item, close, null)\n return item\n}\n","import {\n createIcon,\n createSvgIconNode,\n type IconDefinition,\n type SvgIconDefinition,\n type VobsIconProps\n} from '@vobs/icon-core'\nimport { state } from '@vobs/vobs'\nimport { hasProp, mountSlot, readProp } from './utils'\nimport type { VobsNode } from '@vobs/vobs'\nimport type { VuiCommonProps } from './types'\n\nexport type { IconDefinition } from '@vobs/icon-core'\n\nexport interface IconProps extends VuiCommonProps, VobsIconProps {\n readonly name?: string\n readonly icon?: IconDefinition\n}\n\nexport function Icon(props: IconProps = {}): VobsNode {\n const root = createSvgIconNode(() => {\n // 读取注册表版本号:registerIcon 后已挂载的 Icon 自动补渲染,\n // 支持异步/按需注册(如 Iconify 集合、在线加载)。\n registryVersion.value\n const definition = resolveIcon(\n readProp<IconDefinition | string | undefined>(props, 'icon', undefined)\n ?? readProp<string | undefined>(props, 'name', undefined)\n )\n if (!definition) {\n if (hasProp(props, 'children')) return undefined\n const requested = readProp<IconDefinition | string | undefined>(props, 'icon', undefined)\n ?? readProp<string | undefined>(props, 'name', undefined)\n console.warn(`Vobs UI: 未注册的图标 \"${String(requested)}\",已渲染为空。请先 registerIcon 或改用已注册的名称。`)\n return undefined\n }\n return iconDefinitionToSvg(definition)\n }, props, {\n class: 'vui-icon icon',\n defaultSvgAttributes: {\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': 2,\n 'stroke-linecap': 'butt',\n 'stroke-linejoin': 'miter'\n }\n }) as Element\n\n if (hasProp(props, 'children')) mountSlot(root, props, 'children')\n return root\n}\n\nfunction iconDefinitionToSvg(definition: IconDefinition): SvgIconDefinition {\n return {\n name: definition.name,\n body: definition.path,\n viewBox: definition.viewBox\n }\n}\n\nconst VUI_ICON_PATHS = {\n search: '<circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m20 20-3.5-3.5\"/>',\n check: '<polyline points=\"4 12 10 18 20 6\"/>',\n x: '<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>',\n plus: '<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n minus: '<line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/>',\n 'chevron-right': '<polyline points=\"9 6 15 12 9 18\"/>',\n 'chevron-down': '<polyline points=\"6 9 12 15 18 9\"/>',\n 'chevron-up': '<polyline points=\"6 15 12 9 18 15\"/>',\n 'chevron-left': '<polyline points=\"15 6 9 12 15 18\"/>',\n 'arrow-left': '<line x1=\"20\" y1=\"12\" x2=\"4\" y2=\"12\"/><polyline points=\"10 6 4 12 10 18\"/>',\n 'arrow-right': '<line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/><polyline points=\"14 6 20 12 14 18\"/>',\n 'arrow-up': '<line x1=\"12\" y1=\"20\" x2=\"12\" y2=\"4\"/><polyline points=\"6 10 12 4 18 10\"/>',\n 'arrow-down': '<line x1=\"12\" y1=\"4\" x2=\"12\" y2=\"20\"/><polyline points=\"6 14 12 20 18 14\"/>',\n atom: '<circle cx=\"12\" cy=\"12\" r=\"2\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\" transform=\"rotate(60 12 12)\"/><ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"4\" transform=\"rotate(120 12 12)\"/>',\n globe: '<circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18\"/>',\n 'arrow-expand': '<polyline points=\"14 4 20 4 20 10\"/><line x1=\"14\" y1=\"10\" x2=\"20\" y2=\"4\"/><polyline points=\"10 20 4 20 4 14\"/><line x1=\"10\" y1=\"14\" x2=\"4\" y2=\"20\"/>',\n 'arrow-minimize': '<polyline points=\"20 4 14 10 20 10\"/><line x1=\"14\" y1=\"10\" x2=\"14\" y2=\"4\"/><polyline points=\"4 20 10 14 4 14\"/><line x1=\"10\" y1=\"14\" x2=\"10\" y2=\"20\"/>',\n 'check-circle': '<circle cx=\"12\" cy=\"12\" r=\"9\"/><polyline points=\"8 12 11 15 16 9\"/>',\n 'alert-circle': '<circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"16.01\"/>',\n 'alert-triangle': '<path d=\"M12 3 22 20 2 20 Z\"/><line x1=\"12\" y1=\"10\" x2=\"12\" y2=\"15\"/><line x1=\"12\" y1=\"18\" x2=\"12\" y2=\"18.01\"/>',\n info: '<circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"8.01\"/>',\n home: '<polygon points=\"3 11 12 3 21 11 21 21 14 21 14 14 10 14 10 21 3 21 3 11\"/>',\n user: '<path d=\"M4 21v-1a6 6 0 0 1 6-6h4a6 6 0 0 1 6 6v1\"/><circle cx=\"12\" cy=\"8\" r=\"4\"/>',\n settings: '<circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z\"/>',\n menu: '<line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/><line x1=\"4\" y1=\"18\" x2=\"20\" y2=\"18\"/>',\n 'more-h': '<circle cx=\"5\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"19\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/>',\n calendar: '<rect x=\"3\" y=\"5\" width=\"18\" height=\"16\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/><line x1=\"8\" y1=\"3\" x2=\"8\" y2=\"7\"/><line x1=\"16\" y1=\"3\" x2=\"16\" y2=\"7\"/>',\n filter: '<polygon points=\"3 4 21 4 14 12 14 20 10 18 10 12 3 4\"/>',\n sort: '<path d=\"M8 20V4\"/><polyline points=\"4 8 8 4 12 8\"/><path d=\"M16 4v16\"/><polyline points=\"12 16 16 20 20 16\"/>',\n edit: '<path d=\"M4 20h4l10-10-4-4L4 16v4z\"/><path d=\"M14 6l4 4\"/>',\n trash: '<polyline points=\"4 6 20 6\"/><path d=\"M6 6v14h12V6\"/><path d=\"M9 6V4h6v2\"/><line x1=\"10\" y1=\"10\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"10\" x2=\"14\" y2=\"17\"/>',\n download: '<path d=\"M12 3v12\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"21\"/>',\n upload: '<path d=\"M12 21V6\"/><polyline points=\"7 11 12 6 17 11\"/><line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"21\"/>',\n pause: '<line x1=\"9\" y1=\"5\" x2=\"9\" y2=\"19\"/><line x1=\"15\" y1=\"5\" x2=\"15\" y2=\"19\"/>',\n play: '<polygon points=\"8 5 19 12 8 19 8 5\"/>',\n refresh: '<polyline points=\"21 4 21 10 15 10\"/><polyline points=\"3 20 3 14 9 14\"/><path d=\"M20.5 9A9 9 0 0 0 5 5.5L3 7M3.5 15A9 9 0 0 0 19 18.5L21 17\"/>',\n bell: '<path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 8 3 8H3s3-1 3-8z\"/><path d=\"M10 21a2 2 0 0 0 4 0\"/>',\n folder: '<path d=\"M3 6h6l2 3h10v10H3z\"/>',\n code: '<polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/>',\n sparkles: '<path d=\"M12 3l1.8 4.2L18 9l-4.2 1.8L12 15l-1.8-4.2L6 9l4.2-1.8L12 3z\"/><path d=\"M19 14l1 2.2 2.2 1-2.2 1L19 20.4l-1-2.2-2.2-1 2.2-1L19 14z\"/>',\n zap: '<polygon points=\"13 2 4 14 12 14 11 22 20 10 12 10 13 2\"/>',\n sun: '<circle cx=\"12\" cy=\"12\" r=\"4\"/><line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"5\"/><line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"22\"/><line x1=\"2\" y1=\"12\" x2=\"5\" y2=\"12\"/><line x1=\"19\" y1=\"12\" x2=\"22\" y2=\"12\"/><line x1=\"4.6\" y1=\"4.6\" x2=\"6.7\" y2=\"6.7\"/><line x1=\"17.3\" y1=\"17.3\" x2=\"19.4\" y2=\"19.4\"/><line x1=\"4.6\" y1=\"19.4\" x2=\"6.7\" y2=\"17.3\"/><line x1=\"17.3\" y1=\"6.7\" x2=\"19.4\" y2=\"4.6\"/>',\n moon: '<path d=\"M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z\"/>',\n terminal: '<polyline points=\"4 7 9 12 4 17\"/><line x1=\"12\" y1=\"17\" x2=\"20\" y2=\"17\"/>'\n} as const\n\nconst iconRegistry = new Map<string, IconDefinition>(\n Object.entries(VUI_ICON_PATHS).map(([name, path]) => [name, { name, path }])\n)\n\n/** 注册表版本号:registerIcon/注销时递增,驱动已挂载 Icon 的 effect 重取定义。 */\nconst registryVersion = state(0, 'vui.iconRegistry.version')\n\nexport { VUI_ICON_PATHS }\nexport { createIcon, registerIcon, resolveIcon }\n\nfunction registerIcon(icon: IconDefinition): () => void {\n const previous = iconRegistry.get(icon.name)\n iconRegistry.set(icon.name, icon)\n registryVersion.value++\n return () => {\n if (previous) iconRegistry.set(icon.name, previous)\n else iconRegistry.delete(icon.name)\n registryVersion.value++\n }\n}\n\nfunction resolveIcon(icon: string | IconDefinition | undefined): IconDefinition | undefined {\n return typeof icon === 'string' ? iconRegistry.get(icon) : icon\n}\n","import { effect } from '@vobs/reactivity'\nimport {\n addEventListener,\n createFragment,\n createText,\n insertBefore,\n insertDynamic,\n setAttribute,\n setProperty,\n setTextContent,\n type VobsNode\n} from '@vobs/vobs'\nimport type { VuiCommonProps } from './types'\n\ntype VuiProps = VuiCommonProps | object\n\nexport function classNames(...values: readonly (string | false | null | undefined)[]): string {\n return values\n .flatMap(value => typeof value === 'string' ? value.trim().split(/\\s+/u) : [])\n .filter(Boolean)\n .join(' ')\n}\n\nexport function readProp<T>(props: object, name: string, fallback: T): T {\n const value = Reflect.get(props, name)\n return (value === undefined ? fallback : value) as T\n}\n\nexport function hasProp(props: object, name: string): boolean {\n return name in props\n}\n\nexport function bindClass(\n root: Element,\n props: VuiProps,\n ...baseClasses: readonly (string | undefined)[]\n): void {\n effect(() => {\n setAttribute(root, 'class', classNames(\n ...baseClasses,\n readString(props, 'class'),\n readString(props, 'className')\n ))\n })\n}\n\nexport function bindClassList(\n root: Element,\n props: VuiProps,\n readBaseClasses: () => readonly (string | undefined)[]\n): void {\n effect(() => {\n setAttribute(root, 'class', classNames(\n ...readBaseClasses(),\n readString(props, 'class'),\n readString(props, 'className')\n ))\n })\n}\n\nexport function bindCommonAttributes(\n root: Element,\n props: VuiProps,\n skip: readonly string[] = [],\n options: { readonly includeDataAria?: boolean } = {}\n): void {\n const ignored = new Set(skip)\n ignored.add('class')\n ignored.add('className')\n ignored.add('children')\n ignored.add('style')\n\n effect(() => {\n const next = new Map<string, string>()\n for (const name of Object.keys(props)) {\n if (ignored.has(name) || name.startsWith('on')) continue\n if (!isCommonAttribute(name)) continue\n if (options.includeDataAria === false && (name.startsWith('aria-') || name.startsWith('data-'))) continue\n\n const value = Reflect.get(props, name)\n const normalized = normalizeAttribute(name, value)\n if (normalized !== undefined) next.set(normalized.name, normalized.value)\n }\n\n const managed = getManagedAttributes(root)\n for (const name of managed) {\n if (!next.has(name)) removeAttribute(root, name)\n }\n for (const [name, value] of next) setAttribute(root, name, value)\n setManagedAttributes(root, next.keys())\n })\n}\n\nexport function bindUserStyle(root: Element, props: VuiProps): void {\n bindStyle(root, props, () => '')\n}\n\nexport function bindStyle(root: Element, props: VuiProps, createStyle: () => string): void {\n effect(() => {\n const userStyle = readString(props, 'style')\n const internalStyle = createStyle()\n const style = [internalStyle, userStyle].filter(Boolean).join('; ')\n if (style) setAttribute(root, 'style', style)\n else removeAttribute(root, 'style')\n })\n}\n\nexport function bindTextContent(\n node: Text,\n read: () => unknown\n): void {\n effect(() => {\n setTextContent(node, String(read() ?? ''))\n })\n}\n\nexport function bindPropertyValue(\n node: Element,\n name: string,\n read: () => unknown\n): void {\n effect(() => setProperty(node, name, read()))\n}\n\nexport function bindAttributeValue(\n node: Element,\n name: string,\n read: () => unknown\n): void {\n effect(() => {\n const value = read()\n if (value === undefined || value === null || value === false) removeAttribute(node, name)\n else setAttribute(node, name, String(value))\n })\n}\n\nexport function listen(\n root: Element,\n event: string,\n props: object,\n propName: string,\n disabled?: () => boolean\n): void {\n addEventListener(root, event, (reason: Event) => {\n if (disabled?.()) return\n const handler = Reflect.get(props, propName)\n if (typeof handler === 'function') handler(reason)\n })\n}\n\nexport function mountSlot(\n parent: Node,\n props: object,\n propName: string\n): void {\n insertDynamic(parent, null, () => resolveSlot(Reflect.get(props, propName)))\n}\n\nexport function resolveSlot(value: unknown): VobsNode | null {\n const resolved = typeof value === 'function' ? value() : value\n if (resolved === undefined || resolved === null || resolved === false) return null\n if (typeof resolved === 'string' || typeof resolved === 'number') return createText(String(resolved))\n if (Array.isArray(resolved)) {\n const children = resolved\n return createFragment((parent, anchor) => {\n for (const child of children) {\n const node = resolveSlot(child)\n if (node) insertBefore(parent, node, anchor)\n }\n })\n }\n return resolved as VobsNode\n}\n\nexport function setOptionalAttribute(node: Element, name: string, value: unknown): void {\n if (value === undefined || value === null || value === false || value === '') {\n removeAttribute(node, name)\n return\n }\n setAttribute(node, name, String(value))\n}\n\nexport function setOptionalProperty(node: Element, name: string, value: unknown): void {\n if (value === undefined || value === null) return\n setProperty(node, name, value)\n}\n\nfunction readString(props: object, name: string): string | undefined {\n const value = Reflect.get(props, name)\n return typeof value === 'string' ? value : undefined\n}\n\nfunction isCommonAttribute(name: string): boolean {\n return name === 'id'\n || name === 'title'\n || name === 'role'\n || name === 'tabIndex'\n || name.startsWith('aria-')\n || name.startsWith('data-')\n}\n\nfunction normalizeAttribute(name: string, value: unknown): { name: string; value: string } | undefined {\n if (value === undefined || value === null) return undefined\n if (value === false && !name.startsWith('aria-') && !name.startsWith('data-')) return undefined\n return { name: name === 'tabIndex' ? 'tabindex' : name, value: String(value) }\n}\n\nconst managedAttributes = new WeakMap<object, Set<string>>()\n\nfunction getManagedAttributes(node: Element): Set<string> {\n return managedAttributes.get(node) ?? new Set<string>()\n}\n\nfunction setManagedAttributes(node: Element, names: IterableIterator<string>): void {\n managedAttributes.set(node, new Set(names))\n}\n\nfunction removeAttribute(node: Element, name: string): void {\n const candidate = node as Element & { removeAttribute?: (attribute: string) => void }\n candidate.removeAttribute?.(name)\n}\n","import { getCurrentOwner, onDispose } from '@vobs/reactivity'\nimport {\n createComment,\n insertBefore,\n isVobsFragment,\n type VobsNode\n} from '@vobs/vobs'\n\nexport interface VuiPortalAdapter<Target = unknown> {\n mount(node: VobsNode, target: Target): void\n unmount(node: VobsNode, target: Target): void\n}\n\nexport function createPortal<Target>(\n node: VobsNode,\n adapter: VuiPortalAdapter<Target>,\n target: Target\n): VobsNode {\n const placeholder = createComment('vui:portal')\n const owner = getCurrentOwner()\n if (!owner) throw new Error('Vobs UI: createPortal 必须在组件 Owner 作用域内调用')\n adapter.mount(node, target)\n const cleanup = (): void => adapter.unmount(node, target)\n owner.onDispose(cleanup)\n return placeholder\n}\n\nexport function createDOMPortalAdapter(): VuiPortalAdapter<Node> {\n return {\n mount(node, target): void {\n insertBefore(target, node, null)\n },\n unmount(node, target): void {\n removePortalNode(node, target)\n }\n }\n}\n\nexport interface FocusTrapOptions {\n readonly initialFocus?: HTMLElement | (() => HTMLElement | null)\n readonly restoreFocus?: boolean\n readonly onEscape?: (event: KeyboardEvent) => void\n}\n\nexport interface FocusTrapHandle {\n readonly active: boolean\n activate(): void\n deactivate(): void\n dispose(): void\n}\n\nexport function createFocusTrap(\n root: HTMLElement,\n options: FocusTrapOptions = {}\n): FocusTrapHandle {\n let isActive = false\n let disposed = false\n let previouslyFocused: Element | null = null\n\n const focusable = (): HTMLElement[] => [...root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)]\n .filter(element => !element.hasAttribute('disabled') && element.getAttribute('aria-hidden') !== 'true')\n\n const onKeyDown = (event: KeyboardEvent): void => {\n if (!isActive || disposed) return\n if (event.key === 'Escape') {\n options.onEscape?.(event)\n return\n }\n if (event.key !== 'Tab') return\n const elements = focusable()\n if (elements.length === 0) {\n event.preventDefault()\n root.focus()\n return\n }\n const first = elements[0]\n const last = elements[elements.length - 1]\n const current = document.activeElement\n if (event.shiftKey && current === first) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && current === last) {\n event.preventDefault()\n first.focus()\n }\n }\n\n const activate = (): void => {\n if (disposed || isActive) return\n isActive = true\n previouslyFocused = document.activeElement\n root.addEventListener('keydown', onKeyDown)\n const initial = resolveInitialFocus(options.initialFocus) ?? focusable()[0] ?? root\n if (!root.hasAttribute('tabindex') && initial === root) root.setAttribute('tabindex', '-1')\n initial.focus()\n }\n\n const deactivate = (): void => {\n if (!isActive) return\n isActive = false\n root.removeEventListener('keydown', onKeyDown)\n if (options.restoreFocus !== false\n && typeof HTMLElement !== 'undefined'\n && previouslyFocused instanceof HTMLElement) previouslyFocused.focus()\n previouslyFocused = null\n }\n\n const handle: FocusTrapHandle = {\n get active(): boolean { return isActive },\n activate,\n deactivate,\n dispose(): void {\n if (disposed) return\n deactivate()\n disposed = true\n }\n }\n if (getCurrentOwner()) onDispose(() => handle.dispose())\n return handle\n}\n\nexport interface OverlayEntry {\n readonly id: string\n readonly modal?: boolean\n readonly closeOnEscape?: boolean\n readonly onEscape?: () => void\n}\n\nexport interface OverlayManager {\n readonly entries: readonly OverlayEntry[]\n open(entry: OverlayEntry): void\n close(id: string): void\n bringToFront(id: string): void\n subscribe(listener: (entries: readonly OverlayEntry[]) => void): () => void\n}\n\nexport function createOverlayManager(): OverlayManager {\n let entries: OverlayEntry[] = []\n const listeners = new Set<(entries: readonly OverlayEntry[]) => void>()\n const notify = (): void => {\n const snapshot = entries.slice()\n for (const listener of listeners) listener(snapshot)\n }\n\n return {\n get entries(): readonly OverlayEntry[] { return entries },\n open(entry): void {\n entries = [...entries.filter(current => current.id !== entry.id), entry]\n notify()\n },\n close(id): void {\n const next = entries.filter(entry => entry.id !== id)\n if (next.length === entries.length) return\n entries = next\n notify()\n },\n bringToFront(id): void {\n const entry = entries.find(current => current.id === id)\n if (!entry || entries[entries.length - 1] === entry) return\n entries = [...entries.filter(current => current !== entry), entry]\n notify()\n },\n subscribe(listener): () => void {\n listeners.add(listener)\n listener(entries.slice())\n return () => listeners.delete(listener)\n }\n }\n}\n\nfunction resolveInitialFocus(\n target: FocusTrapOptions['initialFocus']\n): HTMLElement | null {\n if (!target) return null\n return typeof target === 'function' ? target() : target\n}\n\nfunction removePortalNode(node: VobsNode, target: Node): void {\n if (isVobsFragment(node)) {\n let current = node.start.nextSibling\n while (current && current !== node.end) {\n const next = current.nextSibling\n target.removeChild(current)\n current = next\n }\n if (node.start.parentNode === target) target.removeChild(node.start)\n if (node.end.parentNode === target) target.removeChild(node.end)\n return\n }\n if (node.parentNode === target) target.removeChild(node)\n}\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button',\n 'input',\n 'select',\n 'textarea',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]'\n].join(',')\n"],"mappings":"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","names":["addEventListener","createText","insertBefore","setAttribute","insertBefore","setAttribute","addEventListener","insertBefore","createText"]}
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "type": "git",
12
12
  "url": "git+https://github.com/vobsjs/vobs.git"
13
13
  },
14
- "version": "1.3.1",
14
+ "version": "1.3.3",
15
15
  "publishConfig": {
16
16
  "access": "public"
17
17
  },
@@ -201,10 +201,10 @@
201
201
  "./src/styles/*.css"
202
202
  ],
203
203
  "dependencies": {
204
- "@vobs/icon-core": "1.3.1",
205
- "@vobs/reactivity": "1.3.1",
206
- "@vobs/notification": "1.3.1",
207
- "@vobs/vobs": "1.3.1"
204
+ "@vobs/notification": "1.3.3",
205
+ "@vobs/icon-core": "1.3.3",
206
+ "@vobs/reactivity": "1.3.3",
207
+ "@vobs/vobs": "1.3.3"
208
208
  },
209
209
  "scripts": {
210
210
  "test": "vitest --environment jsdom"
package/src/icon.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  type SvgIconDefinition,
6
6
  type VobsIconProps
7
7
  } from '@vobs/icon-core'
8
+ import { state } from '@vobs/vobs'
8
9
  import { hasProp, mountSlot, readProp } from './utils'
9
10
  import type { VobsNode } from '@vobs/vobs'
10
11
  import type { VuiCommonProps } from './types'
@@ -18,13 +19,19 @@ export interface IconProps extends VuiCommonProps, VobsIconProps {
18
19
 
19
20
  export function Icon(props: IconProps = {}): VobsNode {
20
21
  const root = createSvgIconNode(() => {
22
+ // 读取注册表版本号:registerIcon 后已挂载的 Icon 自动补渲染,
23
+ // 支持异步/按需注册(如 Iconify 集合、在线加载)。
24
+ registryVersion.value
21
25
  const definition = resolveIcon(
22
26
  readProp<IconDefinition | string | undefined>(props, 'icon', undefined)
23
27
  ?? readProp<string | undefined>(props, 'name', undefined)
24
28
  )
25
29
  if (!definition) {
26
30
  if (hasProp(props, 'children')) return undefined
27
- throw new Error('Vobs UI: Icon requires a registered `name` or an `icon` definition')
31
+ const requested = readProp<IconDefinition | string | undefined>(props, 'icon', undefined)
32
+ ?? readProp<string | undefined>(props, 'name', undefined)
33
+ console.warn(`Vobs UI: 未注册的图标 "${String(requested)}",已渲染为空。请先 registerIcon 或改用已注册的名称。`)
34
+ return undefined
28
35
  }
29
36
  return iconDefinitionToSvg(definition)
30
37
  }, props, {
@@ -101,15 +108,20 @@ const iconRegistry = new Map<string, IconDefinition>(
101
108
  Object.entries(VUI_ICON_PATHS).map(([name, path]) => [name, { name, path }])
102
109
  )
103
110
 
111
+ /** 注册表版本号:registerIcon/注销时递增,驱动已挂载 Icon 的 effect 重取定义。 */
112
+ const registryVersion = state(0, 'vui.iconRegistry.version')
113
+
104
114
  export { VUI_ICON_PATHS }
105
115
  export { createIcon, registerIcon, resolveIcon }
106
116
 
107
117
  function registerIcon(icon: IconDefinition): () => void {
108
118
  const previous = iconRegistry.get(icon.name)
109
119
  iconRegistry.set(icon.name, icon)
120
+ registryVersion.value++
110
121
  return () => {
111
122
  if (previous) iconRegistry.set(icon.name, previous)
112
123
  else iconRegistry.delete(icon.name)
124
+ registryVersion.value++
113
125
  }
114
126
  }
115
127