@dou-ui/dashboard 0.1.0-alpha.4 → 0.1.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/chart/chart-settings.js.map +1 -1
  2. package/dist/chart/chart-settings.vue_vue_type_script_setup_true_lang.js +10 -8
  3. package/dist/chart/chart-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  4. package/dist/chart/chart.js.map +1 -1
  5. package/dist/chart/chart.vue_vue_type_script_setup_true_lang.js +19 -15
  6. package/dist/chart/chart.vue_vue_type_script_setup_true_lang.js.map +1 -1
  7. package/dist/chart/read-props.js +26 -0
  8. package/dist/chart/read-props.js.map +1 -0
  9. package/dist/dashboard/dashboard.js.map +1 -1
  10. package/dist/dashboard/dashboard.vue_vue_type_script_setup_true_lang.js +27 -4
  11. package/dist/dashboard/dashboard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  12. package/dist/dashboard/error-boundary.js +64 -0
  13. package/dist/dashboard/error-boundary.js.map +1 -0
  14. package/dist/dashboard/frame-context.js.map +1 -1
  15. package/dist/dashboard/widget-body.js.map +1 -1
  16. package/dist/dashboard/widget-body.vue_vue_type_script_setup_true_lang.js +53 -12
  17. package/dist/dashboard/widget-body.vue_vue_type_script_setup_true_lang.js.map +1 -1
  18. package/dist/dashboard/widget-frame.js.map +1 -1
  19. package/dist/dashboard/widget-frame.vue_vue_type_script_setup_true_lang.js +4 -2
  20. package/dist/dashboard/widget-frame.vue_vue_type_script_setup_true_lang.js.map +1 -1
  21. package/dist/dashboard/widget-settings.js.map +1 -1
  22. package/dist/dashboard/widget-settings.vue_vue_type_script_setup_true_lang.js +31 -5
  23. package/dist/dashboard/widget-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/locale.js +6 -0
  26. package/dist/locale.js.map +1 -1
  27. package/dist/pivot/pivot-settings.js.map +1 -1
  28. package/dist/pivot/pivot-settings.vue_vue_type_script_setup_true_lang.js +6 -4
  29. package/dist/pivot/pivot-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  30. package/dist/pivot/pivot.js.map +1 -1
  31. package/dist/pivot/pivot.vue_vue_type_script_setup_true_lang.js +4 -2
  32. package/dist/pivot/pivot.vue_vue_type_script_setup_true_lang.js.map +1 -1
  33. package/dist/pivot/read-props.js +44 -0
  34. package/dist/pivot/read-props.js.map +1 -0
  35. package/dist/style.css +1 -1
  36. package/dist/types/chart/read-props.d.ts +3 -0
  37. package/dist/types/dashboard/dashboard.vue.d.ts +2 -0
  38. package/dist/types/dashboard/error-boundary.d.ts +12 -0
  39. package/dist/types/dashboard/frame-context.d.ts +2 -0
  40. package/dist/types/dashboard/types.d.ts +17 -0
  41. package/dist/types/dashboard/widget-body.vue.d.ts +2 -0
  42. package/dist/types/dashboard/widget-settings.vue.d.ts +4 -0
  43. package/dist/types/index.d.ts +3 -1
  44. package/dist/types/locale.d.ts +4 -0
  45. package/dist/types/pivot/read-props.d.ts +3 -0
  46. package/dist/types/widgets/index.d.ts +1 -1
  47. package/dist/types/widgets/read-props.d.ts +4 -0
  48. package/dist/types/widgets/shared.d.ts +20 -4
  49. package/dist/widgets/index.js +2 -2
  50. package/dist/widgets/index.js.map +1 -1
  51. package/dist/widgets/read-props.js +52 -0
  52. package/dist/widgets/read-props.js.map +1 -0
  53. package/dist/widgets/shared.js +27 -5
  54. package/dist/widgets/shared.js.map +1 -1
  55. package/dist/widgets/stat-settings.js.map +1 -1
  56. package/dist/widgets/stat-settings.vue_vue_type_script_setup_true_lang.js +12 -10
  57. package/dist/widgets/stat-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  58. package/dist/widgets/stat.js.map +1 -1
  59. package/dist/widgets/stat.vue_vue_type_script_setup_true_lang.js +10 -13
  60. package/dist/widgets/stat.vue_vue_type_script_setup_true_lang.js.map +1 -1
  61. package/dist/widgets/table-settings.js.map +1 -1
  62. package/dist/widgets/table-settings.vue_vue_type_script_setup_true_lang.js +6 -4
  63. package/dist/widgets/table-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  64. package/dist/widgets/table.js.map +1 -1
  65. package/dist/widgets/table.vue_vue_type_script_setup_true_lang.js +3 -1
  66. package/dist/widgets/table.vue_vue_type_script_setup_true_lang.js.map +1 -1
  67. package/dist/widgets/text-settings.js.map +1 -1
  68. package/dist/widgets/text-settings.vue_vue_type_script_setup_true_lang.js +5 -3
  69. package/dist/widgets/text-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  70. package/dist/widgets/text.js.map +1 -1
  71. package/dist/widgets/text.vue_vue_type_script_setup_true_lang.js +5 -3
  72. package/dist/widgets/text.vue_vue_type_script_setup_true_lang.js.map +1 -1
  73. package/package.json +8 -8
@@ -81,6 +81,7 @@ var dashboard_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
81
81
  "change",
82
82
  "breakpointChange",
83
83
  "exportError",
84
+ "widgetError",
84
85
  "update:comments",
85
86
  "update:activeComment",
86
87
  "commentAction"
@@ -646,7 +647,12 @@ var dashboard_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
646
647
  exporting: (id) => exporting.has(id),
647
648
  setDrawn: (id, drawn) => drawn ? drawnIds.add(id) : drawnIds.delete(id),
648
649
  exportError: (id) => exportErrors.get(id),
649
- clearExportError: (id) => exportErrors.delete(id)
650
+ clearExportError: (id) => exportErrors.delete(id),
651
+ reportError: (id, error) => emit("widgetError", {
652
+ widget: id,
653
+ part: "content",
654
+ error
655
+ })
650
656
  });
651
657
  /** How each mounted widget gives its content as a workbook; see `setWorkbook`. */
652
658
  const workbooks = shallowReactive(/* @__PURE__ */ new Map());
@@ -1093,7 +1099,13 @@ var dashboard_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
1093
1099
  key: 0,
1094
1100
  widget: settingsWidget.value,
1095
1101
  type: typeOf(settingsWidget.value),
1102
+ messages: unref(messages),
1096
1103
  "set-props": setProps,
1104
+ "report-error": (id, error) => emit("widgetError", {
1105
+ widget: id,
1106
+ part: "settings",
1107
+ error
1108
+ }),
1097
1109
  "onUpdate:props": _cache[3] || (_cache[3] = (value) => run([{
1098
1110
  type: "update",
1099
1111
  id: settingsWidget.value.id,
@@ -1102,7 +1114,12 @@ var dashboard_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
1102
1114
  origin: "settings",
1103
1115
  group: `props:${settingsWidget.value.id}`
1104
1116
  }))
1105
- }, null, 8, ["widget", "type"])) : createCommentVNode("", true)])) : createCommentVNode("", true)]),
1117
+ }, null, 8, [
1118
+ "widget",
1119
+ "type",
1120
+ "messages",
1121
+ "report-error"
1122
+ ])) : createCommentVNode("", true)])) : createCommentVNode("", true)]),
1106
1123
  _: 1
1107
1124
  }, 8, ["open", "title"]),
1108
1125
  createVNode(unref(DouDialog), {
@@ -1136,11 +1153,17 @@ var dashboard_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
1136
1153
  editing: false,
1137
1154
  fullscreen: "",
1138
1155
  messages: unref(messages),
1139
- "set-props": setProps
1156
+ "set-props": setProps,
1157
+ "report-error": (id, error) => emit("widgetError", {
1158
+ widget: id,
1159
+ part: "content",
1160
+ error
1161
+ })
1140
1162
  }, null, 8, [
1141
1163
  "widget",
1142
1164
  "type",
1143
- "messages"
1165
+ "messages",
1166
+ "report-error"
1144
1167
  ]),
1145
1168
  unref(comments).enabled.value ? (openBlock(), createBlock(comment_card_default, {
1146
1169
  key: 1,
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createElementBlock","_normalizeClass","_unref","_createVNode","GridLayout","_normalizeStyle","_withCtx","WidgetFrame","_createCommentVNode","_createElementVNode","_toDisplayString","_createBlock","CommentCard","WidgetSettings","CommentButton","WidgetBody"],"sources":["../../src/dashboard/dashboard.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { LayoutGridPlusIcon, fileIconOf } from '@dou-ui/icons'\nimport type { SheetExporter, SheetWorkbook } from '@dou-ui/sheet/core'\nimport {\n DouDialog,\n DouDrawer,\n DouEmpty,\n DouInput,\n downloadBlob,\n matchesHotkey,\n useLocale,\n} from 'dou-ui'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n shallowReactive,\n shallowRef,\n toRaw,\n watch,\n} from 'vue'\nimport { emptyDocument } from '../core/document'\nimport { DashboardHistory } from '../core/history'\nimport {\n applySteps,\n cloneJson,\n DashboardTransaction,\n replaceWidgets,\n sameValue,\n} from '../core/transaction'\nimport type { DashboardStep, DashboardTransactionMeta } from '../core/transaction'\nimport type { DashboardDocument, DashboardPlace, DashboardWidget } from '../core/types'\nimport type {\n DashboardPeer,\n DashboardPresence,\n DashboardPresenceAction,\n} from '../core/collaboration'\nimport { dataContextKey } from '../data/context'\nimport { filterText, isEmptyFilter } from '../data/filters'\nimport { createSourceStore, sameSource } from '../data/store'\nimport type {\n DashboardFilters,\n DashboardLinkedFilter,\n DashboardSetFilterOptions,\n} from '../data/types'\nimport GridLayout from '../grid/grid-layout.vue'\nimport { DEFAULT_BREAKPOINTS, DEFAULT_COLUMNS, gridTiers } from '../grid/tiers'\nimport type { GridDragEvent, GridDropEvent, GridLayoutExpose, GridLayouts } from '../grid/types'\nimport { findPlace, fitItem, moveItem } from '../layout/engine'\nimport type { GridItem, GridLayoutOptions } from '../layout/types'\nimport { format, useDashboardMessages } from '../locale'\nimport { DEFAULT_WIDGET_SIZE, defaultPropsOf } from '../widget/define'\nimport type { DashboardWidgetType } from '../widget/define'\nimport type {\n DashboardAddOptions,\n DashboardEmits,\n DashboardExportErrorEvent,\n DashboardExportOptions,\n DashboardExpose,\n DashboardSaveOptions,\n DashboardProps,\n} from './types'\nimport { createObservers, observersKey } from './observers'\nimport { elementToPNG } from './export'\nimport type { DashboardImageOptions } from './export'\nimport WidgetBody from './widget-body.vue'\nimport WidgetFrame from './widget-frame.vue'\nimport WidgetSettings from './widget-settings.vue'\nimport { frameContextKey } from './frame-context'\nimport CommentButton from './comment-button.vue'\nimport CommentCard from './comment-card.vue'\nimport { useDashboardComments } from './comments'\nimport type { FrameExporter, WidgetAction } from './frame-context'\n\ndefineOptions({ name: 'DouDashboard' })\n\nconst props = withDefaults(defineProps<Omit<DashboardProps, 'modelValue' | 'filters'>>(), {\n widgets: () => [],\n columns: () => DEFAULT_COLUMNS,\n breakpoints: () => DEFAULT_BREAKPOINTS,\n rowHeight: 80,\n gap: 16,\n compact: 'vertical',\n comments: undefined,\n author: undefined,\n activeComment: undefined,\n commentable: true,\n})\nconst emit = defineEmits<Omit<DashboardEmits, 'update:modelValue' | 'update:filters'>>()\nconst model = defineModel<DashboardDocument>({ default: emptyDocument })\nconst filters = defineModel<DashboardFilters>('filters', { default: () => ({}) })\nconst messages = useDashboardMessages(() => props.messages)\n\nconst grid = ref<GridLayoutExpose>()\nconst observers = createObservers()\nprovide(observersKey, observers)\nonBeforeUnmount(() => observers.dispose())\n/** This many widgets are drawn in full at once, the rest when they come near the view. */\nconst EAGER = 16\nconst root = ref<HTMLElement>()\n\nconst typeMap = computed(() => new Map(props.widgets.map((type) => [type.type, type])))\nconst typeOf = (widget: DashboardWidget) => typeMap.value.get(widget.type)\nconst widgetMap = computed(() => new Map(model.value.widgets.map((widget) => [widget.id, widget])))\n/** The first widgets of the document, drawn in full without waiting to be seen. */\nconst eagerIds = computed(() => new Set(model.value.widgets.slice(0, EAGER).map((w) => w.id)))\n/** Widgets added once the dashboard is up are drawn in full straight away (not reactive on purpose). */\nlet mounted = false\nonMounted(() => (mounted = true))\nconst isEager = (id: string) => mounted || eagerIds.value.has(id)\nconst titleOf = (widget: DashboardWidget) => widget.title || typeOf(widget)?.label || widget.type\n\nconst tiers = computed(() => gridTiers(props.columns, props.breakpoints))\n/** The widest breakpoint: its places are the main ones. */\nconst main = computed(() => tiers.value[0]!)\nconst mainOptions = computed<GridLayoutOptions>(() => ({\n columns: Math.max(1, Math.round(main.value.columns)),\n compact: props.compact,\n}))\n\n// ---------------------------------------------------------------------------\n// Changes and history\n\nconst history = new DashboardHistory()\n/** Bumped whenever the history changes, so `canUndo` and `canRedo` update. */\nconst historyVersion = ref(0)\n/** The document last emitted, to tell our own changes from the app's. */\nlet emitted: DashboardDocument | undefined\nwatch(model, (doc) => {\n // the app may hand back a reactive copy of what was emitted\n // a document the app put in itself: the old steps no longer fit it\n if (toRaw(doc) === emitted) return\n history.clear()\n historyVersion.value++\n})\n\nfunction commit(tr: DashboardTransaction) {\n emitted = toRaw(tr.doc)\n model.value = tr.doc\n emit('change', tr)\n}\n\n/** A transaction on the document as it is now, to `dispatch`. */\nfunction transaction() {\n return new DashboardTransaction(model.value)\n}\n\n/** Makes the changes of a transaction, as one undo step (or part of one, by `meta.group`). */\nfunction dispatch(tr: DashboardTransaction) {\n if (toRaw(tr.before) !== toRaw(model.value))\n throw new Error(\n '[@dou-ui/dashboard] the transaction was made on another version of the dashboard',\n )\n if (!tr.changed) return\n const shared = props.collaboration\n if (!shared) {\n history.record(tr)\n historyVersion.value++\n }\n commit(tr)\n shared?.transaction(tr)\n}\n\n/** Steps made into a transaction and dispatched, for the dashboard's own changes. */\nfunction run(steps: DashboardStep[], meta: DashboardTransactionMeta) {\n const tr = applySteps(model.value, steps, meta)\n dispatch(tr)\n return tr\n}\n\n// ---------------------------------------------------------------------------\n// Comments on widgets\n\nconst comments = useDashboardComments({\n props,\n emit,\n model,\n run,\n carry: (id, patch) => history.carry(id, patch),\n})\n/** The full screen view, which has a comment button of its own. */\nconst fullscreenBody = shallowRef<HTMLElement | null>(null)\n/** A widget's comment button: in the full screen view while that widget is there. */\nfunction buttonOf(id: string) {\n const selector = `[data-dou-comment=\"${CSS.escape(id)}\"]`\n const inFullscreen = fullscreenId.value === id ? fullscreenBody.value : null\n return (\n inFullscreen?.querySelector<HTMLElement>(selector) ??\n root.value?.querySelector<HTMLElement>(selector) ??\n null\n )\n}\n/** Where a widget's card goes: by its comment button, or by the widget until the button is drawn. */\nconst cardSpotOf = (id: string) =>\n buttonOf(id) ??\n root.value?.querySelector<HTMLElement>(`[data-grid-id=\"${CSS.escape(id)}\"]`) ??\n null\n/** The comment shortcut in the full screen view, which is outside the dashboard. */\nfunction onFullscreenKeydown(event: KeyboardEvent) {\n const id = fullscreenId.value\n if (!id || event.defaultPrevented || !comments.enabled.value || isTyping(event.target)) return\n if (matchesHotkey(event, 'mod+alt+m') && comments.start(id)) event.preventDefault()\n}\n// A thread opened from outside, e.g. a list beside the dashboard: bring its widget into view.\nwatch(\n () => props.activeComment,\n (id) => {\n const widget = id && !comments.openedHere(id) ? comments.widgetOf(id) : null\n if (widget) grid.value?.scrollToItem(widget)\n },\n)\n\nfunction undo() {\n if (props.collaboration) return props.collaboration.undo()\n const tr = history.undo(model.value)\n historyVersion.value++\n if (!tr) return false\n if (tr.changed) commit(tr)\n return true\n}\n\nfunction redo() {\n if (props.collaboration) return props.collaboration.redo()\n const tr = history.redo(model.value)\n historyVersion.value++\n if (!tr) return false\n if (tr.changed) commit(tr)\n return true\n}\n\nconst canUndo = computed(() =>\n props.collaboration ? props.collaboration.canUndo() : (historyVersion.value, history.canUndo),\n)\nconst canRedo = computed(() =>\n props.collaboration ? props.collaboration.canRedo() : (historyVersion.value, history.canRedo),\n)\n\n// ---------------------------------------------------------------------------\n// The grid: widgets as items, places by breakpoint\n\nfunction limitsOf(type: DashboardWidgetType<any> | undefined) {\n const size = type?.size ?? DEFAULT_WIDGET_SIZE\n return {\n w: size.w,\n h: size.h,\n minW: size.minW,\n maxW: size.maxW,\n minH: size.minH,\n maxH: size.maxH,\n }\n}\n\n/** The widgets on the widest breakpoint; those without a place there get the first spot they fit. */\nconst placeOf = (item: DashboardPlace): DashboardPlace => ({\n x: item.x,\n y: item.y,\n w: item.w,\n h: item.h,\n})\n\n// The item of a widget that didn't change is the same object as before, so\n// the grid and its other items have nothing to draw again.\nlet itemCache = new WeakMap<DashboardWidget, GridItem>()\nwatch([typeMap, main], () => (itemCache = new WeakMap()), { flush: 'sync' })\nconst items = computed<GridItem[]>(() => {\n const name = main.value.name\n const options = mainOptions.value\n const placed: GridItem[] = []\n const result = new Map<string, GridItem>()\n const missing: DashboardWidget[] = []\n for (const widget of model.value.widgets) {\n const raw = toRaw(widget)\n const place = raw.place[name]\n if (!place) {\n missing.push(raw)\n continue\n }\n let item = itemCache.get(raw)\n if (!item) {\n const { minW, maxW, minH, maxH } = limitsOf(typeOf(raw))\n item = { id: raw.id, ...placeOf(place), minW, maxW, minH, maxH }\n itemCache.set(raw, item)\n }\n placed.push(item)\n result.set(raw.id, item)\n }\n for (const widget of missing) {\n const fitted = fitItem(\n { id: widget.id, x: 0, y: 0, ...limitsOf(typeOf(widget)) },\n options.columns,\n )\n const item = { ...fitted, ...findPlace(placed, fitted, options) }\n placed.push(item)\n result.set(widget.id, item)\n }\n return model.value.widgets.map((widget) => result.get(widget.id)!)\n})\n\n/** The places on the narrower breakpoints. */\nconst layouts = computed<GridLayouts>(() => {\n const result: GridLayouts = {}\n for (const tier of tiers.value.slice(1)) {\n const list = []\n for (const widget of model.value.widgets) {\n const place = widget.place[tier.name]\n if (place) list.push({ id: widget.id, ...place })\n }\n if (list.length) result[tier.name] = list\n }\n return result\n})\n\n// The grid tells a move with `update:modelValue`, `update:layouts` and\n// `drop` one after another; they are gathered into one change.\nlet pending: { items?: GridItem[]; layouts?: GridLayouts; drop?: { id: string; type: string } } = {}\nlet flushing = false\nfunction gather(part: typeof pending) {\n Object.assign(pending, part)\n if (flushing) return\n flushing = true\n queueMicrotask(flush)\n}\n\nfunction flush() {\n flushing = false\n const { items: list, layouts: saved, drop } = pending\n pending = {}\n const doc = model.value\n const places = new Map<string, DashboardWidget['place']>()\n const setPlace = (id: string, name: string, item: DashboardPlace) => {\n let place = places.get(id)\n if (!place) places.set(id, (place = { ...widgetMap.value.get(id)?.place }))\n place[name] = placeOf(item)\n }\n for (const item of list ?? []) setPlace(item.id, main.value.name, item)\n for (const [name, others] of Object.entries(saved ?? {}))\n for (const item of others ?? []) setPlace(item.id, name, item)\n const steps: DashboardStep[] = []\n for (const [id, place] of places) {\n if (widgetMap.value.has(id)) {\n steps.push({ type: 'update', id, patch: { place } })\n } else if (drop?.id === id) {\n const type = typeMap.value.get(drop.type)\n if (type)\n steps.push({ type: 'add', widget: newWidget(id, type, place), index: doc.widgets.length })\n }\n }\n const tr = run(steps, { origin: drop ? 'drop' : 'layout' })\n if (drop && tr.changed) {\n const widget = widgetMap.value.get(drop.id)\n if (widget) say(format(messages.value.added, { item: titleOf(widget) }))\n }\n}\n\nfunction onDrop(event: GridDropEvent) {\n const data = event.data as { type?: string } | undefined\n if (data?.type) gather({ drop: { id: event.item.id, type: data.type } })\n}\n\nconst dragging = ref(false)\n\n// ---------------------------------------------------------------------------\n// Adding, copying and removing\n\nfunction newId(type: string) {\n const used = widgetMap.value\n for (;;) {\n const id = props.createId?.(type) ?? `${type}-${Math.random().toString(36).slice(2, 8)}`\n if (!used.has(id)) return id\n if (props.createId)\n throw new Error(`[@dou-ui/dashboard] createId gave \"${id}\", which is already in use`)\n }\n}\n\nfunction newWidget(\n id: string,\n type: DashboardWidgetType<any>,\n place: DashboardWidget['place'],\n options: Pick<DashboardAddOptions, 'title' | 'props'> = {},\n): DashboardWidget {\n const widget: DashboardWidget = { id, type: type.type, place }\n if (options.title) widget.title = options.title\n const initial = defaultPropsOf(type)\n if (initial || options.props) widget.props = { ...initial, ...options.props }\n return widget\n}\n\n/**\n * Puts a new item on the widest breakpoint, at `at` (pushing the others\n * away) or in the first spot it fits; the steps also move any widget it\n * pushed.\n */\nfunction placeNew(item: GridItem, at: Pick<DashboardPlace, 'x' | 'y'> | undefined) {\n const options = mainOptions.value\n const fitted = fitItem(item, options.columns)\n const spot = at ?? findPlace(items.value, fitted, options)\n const layout = moveItem([...items.value, { ...fitted, ...spot }], item.id, spot, options)\n const steps: DashboardStep[] = []\n let place: DashboardPlace = { ...fitted, ...spot }\n for (const next of layout) {\n if (next.id === item.id) {\n place = placeOf(next)\n continue\n }\n const widget = widgetMap.value.get(next.id)\n const old = widget && items.value.find((other) => other.id === next.id)\n if (widget && old && (old.x !== next.x || old.y !== next.y))\n steps.push({\n type: 'update',\n id: next.id,\n patch: { place: { ...widget.place, [main.value.name]: placeOf(next) } },\n })\n }\n return { place, steps }\n}\n\nfunction addWidget(typeName: string, options: DashboardAddOptions = {}) {\n const type = typeMap.value.get(typeName)\n if (!type) return undefined\n const id = newId(typeName)\n const { place, steps } = placeNew({ id, x: 0, y: 0, ...limitsOf(type) }, options.at)\n const widget = newWidget(id, type, { [main.value.name]: place }, options)\n run([{ type: 'add', widget, index: model.value.widgets.length }, ...steps], {\n origin: 'api',\n })\n say(format(messages.value.added, { item: titleOf(widget) }))\n if (options.focus !== false) void nextTick(() => focusWidget(id))\n return id\n}\n\n/** Adds a copy of a widget (from this dashboard or the clipboard) beside `near`, or in the first free spot. */\nfunction addCopy(source: DashboardWidget, near?: GridItem) {\n const type = typeOf(source)\n const id = newId(source.type)\n const columns = mainOptions.value.columns\n const size = near ?? { ...limitsOf(type), ...source.place[main.value.name] }\n const at = near\n ? near.x + near.w * 2 <= columns\n ? { x: near.x + near.w, y: near.y }\n : { x: near.x, y: near.y + near.h }\n : undefined\n const { place, steps } = placeNew({ ...limitsOf(type), id, x: 0, y: 0, w: size.w, h: size.h }, at)\n const widget: DashboardWidget = {\n ...cloneJson(source),\n id,\n place: { [main.value.name]: place },\n }\n // a copy starts without the original's discussions\n delete widget.comments\n const index = near\n ? model.value.widgets.findIndex((other) => other.id === near.id) + 1\n : model.value.widgets.length\n run([{ type: 'add', widget, index }, ...steps], { origin: 'copy' })\n say(format(messages.value.added, { item: titleOf(widget) }))\n void nextTick(() => focusWidget(id))\n return id\n}\n\nfunction duplicateWidget(id: string) {\n const widget = widgetMap.value.get(id)\n const item = items.value.find((other) => other.id === id)\n return widget && item ? addCopy(widget, item) : undefined\n}\n\nfunction removeWidget(id: string) {\n const list = model.value.widgets\n const index = list.findIndex((widget) => widget.id === id)\n if (index < 0) return\n const widget = list[index]!\n const hadFocus = root.value?.contains(document.activeElement)\n const neighbour = list[index + 1] ?? list[index - 1]\n if (settingsId.value === id) settingsId.value = undefined\n if (fullscreenId.value === id) fullscreenId.value = undefined\n run([{ type: 'remove', id }], { origin: 'api' })\n say(format(messages.value.removed, { item: titleOf(widget) }))\n if (hadFocus) void nextTick(() => (neighbour ? focusWidget(neighbour.id) : root.value?.focus()))\n}\n\nfunction focusWidget(id: string) {\n grid.value?.focusItem(id)\n}\n\n/** The widget being dragged in, which has no widget yet: its name is its type's. */\nlet adding: { id: string; label: string } | undefined\n\nfunction startAdd(event: PointerEvent, typeName: string) {\n const type = typeMap.value.get(typeName)\n if (!type || !props.editable) return\n adding = { id: newId(typeName), label: type.label }\n grid.value?.startDrop(event, { id: adding.id, ...limitsOf(type) }, { type: typeName })\n}\n\nfunction labelOf(item: GridItem) {\n const widget = widgetMap.value.get(item.id)\n return widget ? titleOf(widget) : item.id === adding?.id ? adding.label : item.id\n}\n\nfunction setProps(id: string, patch: Record<string, unknown>, options: { group?: string } = {}) {\n const widget = widgetMap.value.get(id)\n if (!widget) return\n run([{ type: 'update', id, patch: { props: { ...widget.props, ...patch } } }], {\n origin: 'widget',\n group: options.group,\n })\n}\n\nfunction rename(id: string, title: string) {\n run([{ type: 'update', id, patch: { title: title.trim() || undefined } }], {\n origin: 'title',\n })\n}\n\n// ---------------------------------------------------------------------------\n// Settings and full screen\n\nconst settingsId = ref<string>()\nconst settingsWidget = computed(() =>\n settingsId.value ? widgetMap.value.get(settingsId.value) : undefined,\n)\nconst fullscreenId = ref<string>()\nconst fullscreenWidget = computed(() =>\n fullscreenId.value ? widgetMap.value.get(fullscreenId.value) : undefined,\n)\n/** The widget to give focus back to when a drawer or dialog closes. */\nlet returnTo: string | undefined\n\nfunction openSettings(id: string) {\n if (!props.editable || !widgetMap.value.has(id)) return\n returnTo = id\n settingsId.value = id\n}\n\nfunction openFullscreen(id: string) {\n const widget = widgetMap.value.get(id)\n if (!widget || typeOf(widget)?.fullscreen === false) return\n returnTo = id\n fullscreenId.value = id\n}\n\nfunction restoreFocus() {\n const id = returnTo\n returnTo = undefined\n if (id && widgetMap.value.has(id)) focusWidget(id)\n}\n\nwatch(\n () => props.editable,\n (editable) => {\n if (!editable) settingsId.value = undefined\n },\n)\n\nfunction onAction(id: string, action: WidgetAction) {\n if (action === 'settings') openSettings(id)\n else if (action === 'fullscreen') openFullscreen(id)\n else if (action === 'duplicate') duplicateWidget(id)\n else removeWidget(id)\n}\n\n// ---------------------------------------------------------------------------\n// Editing together\n\n/** Changes from others wait while a widget is dragged here; then the shared document is read again. */\nlet held = false\n\nfunction applyShared(doc: DashboardDocument, origin: 'remote' | 'undo' | 'redo') {\n if (dragging.value) {\n held = true\n return\n }\n const tr = replaceWidgets(new DashboardTransaction(model.value), doc, { keepComments: false })\n if (!tr.changed) return\n tr.setMeta('history', false).setMeta('origin', origin)\n const ids = new Set(tr.doc.widgets.map((widget) => widget.id))\n if (settingsId.value && !ids.has(settingsId.value)) settingsId.value = undefined\n if (fullscreenId.value && !ids.has(fullscreenId.value)) fullscreenId.value = undefined\n commit(tr)\n}\n\n// one written in the template is new on every render: each one starts over and clears undo\nlet setupTimes: number[] = []\nlet setupWarned = false\nfunction warnRepeatedSetup() {\n if (process.env.NODE_ENV === 'production' || setupWarned) return\n const now = Date.now()\n setupTimes = [...setupTimes.filter((time) => now - time < 1000), now]\n if (setupTimes.length < 4) return\n setupWarned = true\n console.warn(\n '[@dou-ui/dashboard] collaboration keeps being replaced, which starts it over and clears undo each time. Create it once in <script setup> instead of in the template.',\n )\n}\n\nwatch(\n () => props.collaboration,\n (shared, _, onCleanup) => {\n if (shared) warnRepeatedSetup()\n history.clear()\n historyVersion.value++\n if (!shared) return\n const stop = shared.setup({ document: () => model.value, apply: applyShared })\n onCleanup(() => {\n shared.presence?.(null)\n stop()\n })\n },\n { immediate: true },\n)\n\n/** The widget being moved or resized here, and how. */\nconst drag = shallowRef<GridDragEvent>()\nfunction onDragStart(event: GridDragEvent) {\n dragging.value = true\n drag.value = event\n}\nfunction onDragEnd() {\n dragging.value = false\n drag.value = undefined\n if (!held) return\n held = false\n // after the drop's own change, which the grid hands over in a microtask\n queueMicrotask(() => queueMicrotask(() => props.collaboration?.sync()))\n}\n\n/** The widget whose item, or a control in it, has focus. */\nconst focused = ref<string>()\nfunction onFocusIn(event: FocusEvent) {\n const item = (event.target as HTMLElement).closest?.<HTMLElement>('[data-grid-id]')\n focused.value = item?.closest('.dou-dashboard') === root.value ? item?.dataset.gridId : undefined\n}\nfunction onFocusOut(event: FocusEvent) {\n if (!root.value?.contains(event.relatedTarget as Node | null)) focused.value = undefined\n}\n\n/** What this person is doing, shown to the others. */\nconst presence = computed<DashboardPresence | null>(() => {\n const moving = drag.value\n if (moving && moving.kind !== 'drop' && widgetMap.value.has(moving.id))\n return { widget: moving.id, action: moving.kind as DashboardPresenceAction }\n if (settingsId.value) return { widget: settingsId.value, action: 'settings' }\n if (focused.value && widgetMap.value.has(focused.value))\n return { widget: focused.value, action: 'focus' }\n return null\n})\nwatch(\n [presence, () => props.collaboration] as const,\n ([current, shared], previous) => {\n if (!shared?.presence) return\n const [before, sharedBefore] = previous ?? []\n if (\n sharedBefore === shared &&\n before?.widget === current?.widget &&\n before?.action === current?.action\n )\n return\n shared.presence(current)\n },\n { immediate: true },\n)\nconst NO_PEERS: readonly DashboardPeer[] = []\n\nprovide(frameContextKey, {\n comments,\n widget: (id) => widgetMap.value.get(id),\n type: typeOf,\n title: titleOf,\n editing: () => props.editable,\n messages: () => messages.value,\n eager: isEager,\n all: () => showAll.value,\n setProps,\n action: onAction,\n rename,\n linked: (id) => linksByWidget.value.get(id) ?? NO_LINKS,\n clearFilter: (key) => setFilter(undefined, key, undefined),\n peers: (id) => props.collaboration?.peers?.(id) ?? NO_PEERS,\n setWorkbook,\n exporters: (id) => (workbooks.has(id) ? exporterList.value : NO_EXPORTERS),\n exportTo: exportFromMenu,\n exporting: (id) => exporting.has(id),\n setDrawn: (id, drawn) => (drawn ? drawnIds.add(id) : drawnIds.delete(id)),\n exportError: (id) => exportErrors.get(id),\n clearExportError: (id) => exportErrors.delete(id),\n})\n\n// ---------------------------------------------------------------------------\n// Exporting a widget's content as a file\n\n/** How each mounted widget gives its content as a workbook; see `setWorkbook`. */\nconst workbooks = shallowReactive(new Map<string, () => SheetWorkbook | undefined>())\nfunction setWorkbook(id: string, get: () => SheetWorkbook | undefined) {\n workbooks.set(id, get)\n return () => {\n if (workbooks.get(id) === get) workbooks.delete(id)\n }\n}\n\n// the file code comes with the first exporter, in a chunk of its own\ntype SheetCore = typeof import('@dou-ui/sheet/core')\nconst sheetCore = shallowRef<SheetCore>()\nlet loadingCore: Promise<SheetCore> | undefined\nfunction loadSheetCore() {\n loadingCore ??= import('@dou-ui/sheet/core').then((core) => (sheetCore.value = core))\n return loadingCore\n}\nwatch(\n () => props.exporters?.length,\n (count) => count && void loadSheetCore(),\n { immediate: true },\n)\n\nconst NO_EXPORTERS: FrameExporter[] = []\nconst { locale } = useLocale()\n/** The exporters of the menu, without those that draw a sheet's grid. */\nconst exporterList = computed<FrameExporter[]>(() => {\n const core = sheetCore.value\n if (!core) return NO_EXPORTERS\n const sheetMessages = core.sheetMessagesFor(locale.value.name)\n return (props.exporters ?? [])\n .filter((exporter) => exporter.needs !== 'view')\n .map((exporter) => ({\n name: exporter.name,\n label: typeof exporter.label === 'function' ? exporter.label(sheetMessages) : exporter.label,\n icon: exporter.icon ?? fileIconOf(exporter.extension),\n }))\n})\n\nfunction exporterOf(exporter: string | SheetExporter) {\n if (typeof exporter !== 'string') return exporter\n const found = props.exporters?.find((item) => item.name === exporter)\n if (!found) throw new RangeError(`[@dou-ui/dashboard] there is no exporter \"${exporter}\"`)\n return found\n}\n\n/** A widget's file and sheet name: its title, or its type's name. */\nconst exportNameOf = (widget: DashboardWidget) => titleOf(widget).trim() || widget.type\n\n/** Why something failed, as people read it: a file error's `reason`, or its message. */\nfunction reasonOf(error: unknown) {\n if (error instanceof Error)\n return 'reason' in error && typeof error.reason === 'string' ? error.reason : error.message\n return String(error)\n}\n\n/**\n * A widget's content as a file: its workbook written by the exporter, a\n * name from `exporters` or an exporter itself. Waits for the data shown,\n * and draws the widget first when it is far from the view.\n */\nasync function exportWidget(\n id: string,\n exporter: string | SheetExporter,\n options: DashboardExportOptions = {},\n): Promise<Blob> {\n const chosen = exporterOf(exporter)\n const widget = widgetMap.value.get(id)\n if (!widget) throw new RangeError(`[@dou-ui/dashboard] there is no widget \"${id}\"`)\n const core = await loadSheetCore()\n // a widget drawn already that gives no workbook never will: no need to draw them all\n await settle(5000, !drawnIds.has(id))\n options.signal?.throwIfAborted()\n const get = workbooks.get(id)\n const book = get?.()\n if (!book)\n throw new core.SheetFileError(\n get\n ? `[@dou-ui/dashboard] the widget \"${id}\" has no data to export now`\n : `[@dou-ui/dashboard] the widget \"${id}\" doesn't give its content as a workbook (setWorkbook)`,\n 'empty',\n messages.value.nothingToExport,\n )\n // sheets without a name take the widget's, as the file does\n const name = exportNameOf(widget)\n const named = book.sheets.some((sheet) => !sheet.name)\n ? { ...book, sheets: book.sheets.map((sheet) => (sheet.name ? sheet : { ...sheet, name })) }\n : book\n return core.exportWorkbook(named, chosen, {\n options: options.options,\n signal: options.signal,\n locale: locale.value.name,\n })\n}\n\n/** The widgets drawn in full; the others show only their title until they come near. */\nconst drawnIds = new Set<string>()\n\n/** Why the last export from each widget's menu failed, shown over its content until closed. */\nconst exportErrors = shallowReactive(new Map<string, string>())\n/** The widgets exporting now, whose export button waits. */\nconst exporting = shallowReactive(new Set<string>())\n\n/**\n * Exports a widget's content and saves the file in the downloads, as its\n * menu does. A failure shows over the widget and comes as `exportError`,\n * and the promise rejects.\n */\nasync function saveWidget(\n id: string,\n exporter: string | SheetExporter,\n options: DashboardSaveOptions = {},\n): Promise<void> {\n const chosen = exporterOf(exporter)\n const widget = widgetMap.value.get(id)\n if (!widget) throw new RangeError(`[@dou-ui/dashboard] there is no widget \"${id}\"`)\n const name = options.fileName ?? `${exportNameOf(widget)}.${chosen.extension}`\n exportErrors.delete(id)\n exporting.add(id)\n let blob: Blob\n try {\n blob = await exportWidget(id, chosen, options)\n } catch (error) {\n const event: DashboardExportErrorEvent = {\n widget: id,\n exporter: chosen.name,\n error,\n message: format(messages.value.exportFailed, { name, reason: reasonOf(error) }),\n defaultPrevented: false,\n preventDefault() {\n this.defaultPrevented = true\n },\n }\n emit('exportError', event)\n // the notice is read out itself; told by the app, it is only read out\n if (event.defaultPrevented) say(event.message)\n else exportErrors.set(id, event.message)\n throw error\n } finally {\n exporting.delete(id)\n }\n downloadBlob(blob, name)\n say(format(messages.value.exported, { name }))\n}\n\nfunction exportFromMenu(id: string, name: string) {\n if (exporting.has(id)) return\n // the failure is shown over the widget\n saveWidget(id, name).catch(() => {})\n}\n\n// ---------------------------------------------------------------------------\n// Data and filters\n\nconst store = createSourceStore(() => props.sources)\nonBeforeUnmount(() => store.dispose())\n// a source replaced by one that loads differently loads again\nwatch(\n () => props.sources,\n (next, previous) => {\n const names = new Set([...Object.keys(previous ?? {}), ...Object.keys(next ?? {})])\n for (const name of names) if (!sameSource(previous?.[name], next?.[name])) store.reload(name)\n },\n)\n\n/** The filters set by widgets, by key: they apply to every widget but the one that set them. */\nconst links = shallowRef(new Map<string, DashboardLinkedFilter>())\nconst EMPTY_SET: ReadonlySet<string> = new Set()\nconst NO_LINKS: DashboardLinkedFilter[] = []\nconst linksByWidget = computed(() => {\n const result = new Map<string, DashboardLinkedFilter[]>()\n for (const link of links.value.values()) {\n const list = result.get(link.widget)\n if (list) list.push(link)\n else result.set(link.widget, [link])\n }\n return result\n})\nconst emittedByWidget = computed(() => {\n const result = new Map<string, Set<string>>()\n for (const [id, list] of linksByWidget.value)\n result.set(id, new Set(list.map((link) => link.key)))\n return result\n})\n\n// The values as set last: with `v-model:filters` the model only updates once\n// the parent draws again, so several filters set at once build on this.\nconst values = shallowRef<DashboardFilters>(filters.value)\nwatch(filters, (next) => (values.value = next))\n\nfunction setFilter(\n widget: string | undefined,\n key: string,\n value: unknown,\n options: DashboardSetFilterOptions = {},\n) {\n const next = { ...values.value }\n const nextLinks = new Map(links.value)\n if (isEmptyFilter(value)) delete next[key]\n else next[key] = value\n if (widget && !isEmptyFilter(value) && widgetMap.value.has(widget))\n nextLinks.set(key, { key, value, label: options.label ?? filterText(value), widget })\n else nextLinks.delete(key)\n links.value = nextLinks\n values.value = next\n filters.value = next\n}\n\nfunction clearFilters() {\n links.value = new Map()\n values.value = {}\n filters.value = {}\n}\n\n// a filter changed from outside (the filter bar, the app) is no longer the widget's\nwatch(filters, (current) => {\n let next: Map<string, DashboardLinkedFilter> | undefined\n for (const [key, link] of links.value) {\n if (sameValue(current[key], link.value)) continue\n next ??= new Map(links.value)\n next.delete(key)\n }\n if (next) links.value = next\n})\n\n// the filters of a widget that is removed go with it\nwatch(widgetMap, (map) => {\n for (const link of links.value.values())\n if (!map.has(link.widget)) setFilter(undefined, link.key, undefined)\n})\n\nconst sourceNames = computed(() => Object.keys(props.sources ?? {}))\nprovide(dataContextKey, {\n store,\n sourceNames: () => sourceNames.value,\n filters: () => values.value,\n emitted: (id) => emittedByWidget.value.get(id) ?? EMPTY_SET,\n linked: (id) => linksByWidget.value.get(id) ?? NO_LINKS,\n setFilter,\n})\n\n// ---------------------------------------------------------------------------\n// Keyboard and clipboard\n\nconst CLIPBOARD_KIND = 'dou-dashboard/widget'\n\nfunction isTyping(target: EventTarget | null) {\n const el = target as HTMLElement | null\n if (!el?.tagName) return false\n return (\n el.isContentEditable ||\n el.tagName === 'TEXTAREA' ||\n el.tagName === 'SELECT' ||\n (el.tagName === 'INPUT' &&\n !['checkbox', 'radio', 'button', 'range', 'color'].includes((el as HTMLInputElement).type))\n )\n}\n\n/** The widget an element is in, such as the one holding the focus. */\nfunction widgetHolding(target: EventTarget | null) {\n const el = (target as HTMLElement | null)?.closest?.<HTMLElement>('[data-grid-id]')\n return el && el.closest('.dou-dashboard') === root.value ? el.dataset.gridId : undefined\n}\n\n/** The widget whose item itself has focus (not a control inside it). */\nfunction focusedId(target: EventTarget | null) {\n const el = target as HTMLElement | null\n return el?.dataset?.gridId && el.closest('.dou-dashboard') === root.value\n ? el.dataset.gridId\n : undefined\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.defaultPrevented || isTyping(event.target)) return\n // anywhere in a widget, also while viewing: its discussions\n if (matchesHotkey(event, 'mod+alt+m') && comments.enabled.value) {\n const id = widgetHolding(event.target)\n if (id && comments.start(id)) event.preventDefault()\n return\n }\n if (!props.editable) return\n if (matchesHotkey(event, 'mod+z')) {\n event.preventDefault()\n undo()\n return\n }\n if (matchesHotkey(event, 'mod+shift+z') || matchesHotkey(event, 'mod+y')) {\n event.preventDefault()\n redo()\n return\n }\n const id = focusedId(event.target)\n if (!id || dragging.value) return\n if (event.key === 'Delete' || event.key === 'Backspace') {\n event.preventDefault()\n removeWidget(id)\n } else if (matchesHotkey(event, 'mod+d')) {\n event.preventDefault()\n duplicateWidget(id)\n }\n}\n\nfunction onCopy(event: ClipboardEvent, cut = false) {\n if (isTyping(event.target)) return\n const id = focusedId(event.target)\n const widget = id && widgetMap.value.get(id)\n if (!widget || !event.clipboardData) return\n event.clipboardData.setData('text/plain', JSON.stringify({ kind: CLIPBOARD_KIND, widget }))\n event.preventDefault()\n if (cut && props.editable) removeWidget(widget.id)\n}\n\nfunction onPaste(event: ClipboardEvent) {\n if (!props.editable || isTyping(event.target)) return\n let data: { kind?: string; widget?: DashboardWidget } | undefined\n try {\n data = JSON.parse(event.clipboardData?.getData('text/plain') ?? '')\n } catch {\n return\n }\n const widget = data?.kind === CLIPBOARD_KIND ? data.widget : undefined\n if (!widget?.type || !typeMap.value.has(widget.type)) return\n event.preventDefault()\n const id = focusedId(event.target)\n addCopy(widget, id ? items.value.find((item) => item.id === id) : undefined)\n}\n\n// ---------------------------------------------------------------------------\n// Export and print\n\n/** Every widget drawn in full, not only those near the view. */\nconst showAll = ref(false)\nconst nextFrame = () => new Promise<void>((resolve) => requestAnimationFrame(() => resolve()))\n\n/** Draws every widget (unless `all` is false) and waits for their sources (at most `timeout` ms) and one more frame. */\nasync function settle(timeout = 5000, all = true) {\n if (all) showAll.value = true\n await nextTick()\n const start = performance.now()\n await nextFrame()\n while (store.busy() && performance.now() - start < timeout) await nextFrame()\n await nextFrame()\n await nextFrame()\n}\n\n/** The dashboard as it is shown, every widget drawn, as a PNG image. */\nasync function exportPNG(options?: DashboardImageOptions) {\n await settle()\n const el = root.value!\n // the controls are left out; the copy is made before the class goes\n el.classList.add('is-exporting')\n try {\n return elementToPNG(el, options)\n } finally {\n el.classList.remove('is-exporting')\n }\n}\n\n/** Draws every widget, waits for their data, and opens the browser's print dialog. */\nasync function print() {\n await settle()\n window.print()\n}\n\n// printing from the browser's menu: draw what is far from the view, as far as there is time\nfunction onBeforePrint() {\n showAll.value = true\n}\nonMounted(() => window.addEventListener('beforeprint', onBeforePrint))\nonBeforeUnmount(() => window.removeEventListener('beforeprint', onBeforePrint))\n\n// ---------------------------------------------------------------------------\n// Announcements\n\nconst live = ref('')\nfunction say(text: string) {\n live.value = ''\n requestAnimationFrame(() => (live.value = text))\n}\n\nconst breakpointName = shallowRef(main.value.name)\nfunction onBreakpoint(name: string) {\n breakpointName.value = name\n emit('breakpointChange', name)\n}\n\nconst minHeight = computed(() => {\n const gap = Array.isArray(props.gap) ? props.gap[1] : props.gap\n return `${props.rowHeight * 3 + gap * 2}px`\n})\n\ndefineExpose<DashboardExpose>({\n transaction,\n dispatch,\n undo,\n redo,\n get canUndo() {\n return canUndo.value\n },\n get canRedo() {\n return canRedo.value\n },\n addWidget,\n removeWidget,\n comment: (id = widgetHolding(document.activeElement)) => !!id && comments.start(id),\n getCommentAnchors: comments.anchors,\n duplicateWidget,\n focusWidget,\n openSettings,\n openFullscreen,\n startAdd,\n get types() {\n return props.widgets\n },\n get breakpoint() {\n return grid.value?.breakpoint ?? breakpointName.value\n },\n get editable() {\n return props.editable\n },\n get document() {\n return model.value\n },\n get filters() {\n return values.value\n },\n get linkedFilters() {\n return [...links.value.values()]\n },\n setFilter: (key, value, options) => setFilter(undefined, key, value, options),\n clearFilters,\n refresh: (source) => store.reload(source),\n exportPNG,\n exportWidget: exportWidget as DashboardExpose['exportWidget'],\n saveWidget: saveWidget as DashboardExpose['saveWidget'],\n print,\n})\n</script>\n\n<template>\n <div\n ref=\"root\"\n :class=\"['dou-dashboard', editable && 'is-editable']\"\n role=\"region\"\n :aria-label=\"label || messages.dashboard\"\n tabindex=\"-1\"\n @keydown=\"onKeydown\"\n @copy=\"onCopy($event)\"\n @cut=\"onCopy($event, true)\"\n @paste=\"onPaste\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <GridLayout\n ref=\"grid\"\n :model-value=\"items\"\n :layouts=\"layouts\"\n :columns=\"columns\"\n :breakpoints=\"breakpoints\"\n :row-height=\"rowHeight\"\n :gap=\"gap\"\n :compact=\"compact\"\n :editable=\"editable\"\n :handle=\"true\"\n :item-label=\"labelOf\"\n :messages=\"messages\"\n :style=\"editable && !model.widgets.length ? { minHeight } : undefined\"\n @update:model-value=\"(list) => gather({ items: list })\"\n @update:layouts=\"(next) => gather({ layouts: next })\"\n @drop=\"onDrop\"\n @drag-start=\"onDragStart\"\n @drag-end=\"onDragEnd\"\n @breakpoint-change=\"onBreakpoint\"\n >\n <template #item=\"{ item }\">\n <WidgetFrame :id=\"item.id\" />\n </template>\n </GridLayout>\n <div v-if=\"editable && !model.widgets.length\" class=\"dou-dashboard__empty\">\n <DouEmpty size=\"sm\" :title=\"messages.emptyTitle\" :description=\"messages.emptyHint\">\n <template #icon><LayoutGridPlusIcon /></template>\n </DouEmpty>\n </div>\n <div class=\"dou-dashboard__live\" aria-live=\"polite\" aria-atomic=\"true\">{{ live }}</div>\n <!-- in fullscreen the card goes in the dialog, which keeps the focus to itself -->\n <CommentCard\n v-if=\"comments.enabled.value && !fullscreenWidget\"\n :comments=\"comments\"\n :messages=\"messages\"\n :button-of=\"buttonOf\"\n :place-of=\"cardSpotOf\"\n />\n <DouDrawer\n :open=\"!!settingsWidget\"\n :title=\"\n settingsWidget ? format(messages.settingsTitle, { title: titleOf(settingsWidget) }) : ''\n \"\n :size=\"360\"\n @update:open=\"(open) => !open && (settingsId = undefined)\"\n @closed=\"restoreFocus\"\n >\n <div v-if=\"settingsWidget\" class=\"dou-dashboard__settings\">\n <label class=\"dou-dashboard__field\">\n <span class=\"dou-dashboard__field-label\">{{ messages.titleField }}</span>\n <DouInput\n :model-value=\"settingsWidget.title ?? ''\"\n :placeholder=\"typeOf(settingsWidget)?.label ?? settingsWidget.type\"\n @update:model-value=\"\n (value) =>\n run(\n [\n {\n type: 'update',\n id: settingsWidget!.id,\n patch: { title: value || undefined },\n },\n ],\n { origin: 'settings', group: `title:${settingsWidget!.id}` },\n )\n \"\n />\n </label>\n <WidgetSettings\n v-if=\"typeOf(settingsWidget)?.settings\"\n :widget=\"settingsWidget\"\n :type=\"typeOf(settingsWidget)!\"\n :set-props=\"setProps\"\n @update:props=\"\n (value) =>\n run([{ type: 'update', id: settingsWidget!.id, patch: { props: value } }], {\n origin: 'settings',\n group: `props:${settingsWidget!.id}`,\n })\n \"\n />\n </div>\n </DouDrawer>\n <DouDialog\n :open=\"!!fullscreenWidget\"\n :title=\"fullscreenWidget ? titleOf(fullscreenWidget) : ''\"\n fullscreen\n @update:open=\"(open) => !open && (fullscreenId = undefined)\"\n @closed=\"restoreFocus\"\n @keydown=\"onFullscreenKeydown\"\n >\n <div v-if=\"fullscreenWidget\" ref=\"fullscreenBody\" class=\"dou-dashboard__fullscreen\">\n <div v-if=\"comments.enabled.value\" class=\"dou-dashboard__fullscreen-bar\">\n <CommentButton\n :id=\"fullscreenWidget.id\"\n :comments=\"comments\"\n :messages=\"messages\"\n tooltip-placement=\"left\"\n />\n </div>\n <WidgetBody\n class=\"is-fullscreen\"\n :widget=\"fullscreenWidget\"\n :type=\"typeOf(fullscreenWidget)\"\n :editing=\"false\"\n fullscreen\n :messages=\"messages\"\n :set-props=\"setProps\"\n />\n <CommentCard\n v-if=\"comments.enabled.value\"\n :comments=\"comments\"\n :messages=\"messages\"\n :button-of=\"buttonOf\"\n :place-of=\"cardSpotOf\"\n />\n </div>\n </DouDialog>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"dashboard.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createElementBlock","_normalizeClass","_unref","_createVNode","GridLayout","_normalizeStyle","_withCtx","WidgetFrame","_createCommentVNode","_createElementVNode","_toDisplayString","_createBlock","CommentCard","WidgetSettings","CommentButton","WidgetBody"],"sources":["../../src/dashboard/dashboard.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { LayoutGridPlusIcon, fileIconOf } from '@dou-ui/icons'\nimport type { SheetExporter, SheetWorkbook } from '@dou-ui/sheet/core'\nimport {\n DouDialog,\n DouDrawer,\n DouEmpty,\n DouInput,\n downloadBlob,\n matchesHotkey,\n useLocale,\n} from 'dou-ui'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n provide,\n ref,\n shallowReactive,\n shallowRef,\n toRaw,\n watch,\n} from 'vue'\nimport { emptyDocument } from '../core/document'\nimport { DashboardHistory } from '../core/history'\nimport {\n applySteps,\n cloneJson,\n DashboardTransaction,\n replaceWidgets,\n sameValue,\n} from '../core/transaction'\nimport type { DashboardStep, DashboardTransactionMeta } from '../core/transaction'\nimport type { DashboardDocument, DashboardPlace, DashboardWidget } from '../core/types'\nimport type {\n DashboardPeer,\n DashboardPresence,\n DashboardPresenceAction,\n} from '../core/collaboration'\nimport { dataContextKey } from '../data/context'\nimport { filterText, isEmptyFilter } from '../data/filters'\nimport { createSourceStore, sameSource } from '../data/store'\nimport type {\n DashboardFilters,\n DashboardLinkedFilter,\n DashboardSetFilterOptions,\n} from '../data/types'\nimport GridLayout from '../grid/grid-layout.vue'\nimport { DEFAULT_BREAKPOINTS, DEFAULT_COLUMNS, gridTiers } from '../grid/tiers'\nimport type { GridDragEvent, GridDropEvent, GridLayoutExpose, GridLayouts } from '../grid/types'\nimport { findPlace, fitItem, moveItem } from '../layout/engine'\nimport type { GridItem, GridLayoutOptions } from '../layout/types'\nimport { format, useDashboardMessages } from '../locale'\nimport { DEFAULT_WIDGET_SIZE, defaultPropsOf } from '../widget/define'\nimport type { DashboardWidgetType } from '../widget/define'\nimport type {\n DashboardAddOptions,\n DashboardEmits,\n DashboardExportErrorEvent,\n DashboardExportOptions,\n DashboardExpose,\n DashboardSaveOptions,\n DashboardProps,\n} from './types'\nimport { createObservers, observersKey } from './observers'\nimport { elementToPNG } from './export'\nimport type { DashboardImageOptions } from './export'\nimport WidgetBody from './widget-body.vue'\nimport WidgetFrame from './widget-frame.vue'\nimport WidgetSettings from './widget-settings.vue'\nimport { frameContextKey } from './frame-context'\nimport CommentButton from './comment-button.vue'\nimport CommentCard from './comment-card.vue'\nimport { useDashboardComments } from './comments'\nimport type { FrameExporter, WidgetAction } from './frame-context'\n\ndefineOptions({ name: 'DouDashboard' })\n\nconst props = withDefaults(defineProps<Omit<DashboardProps, 'modelValue' | 'filters'>>(), {\n widgets: () => [],\n columns: () => DEFAULT_COLUMNS,\n breakpoints: () => DEFAULT_BREAKPOINTS,\n rowHeight: 80,\n gap: 16,\n compact: 'vertical',\n comments: undefined,\n author: undefined,\n activeComment: undefined,\n commentable: true,\n})\nconst emit = defineEmits<Omit<DashboardEmits, 'update:modelValue' | 'update:filters'>>()\nconst model = defineModel<DashboardDocument>({ default: emptyDocument })\nconst filters = defineModel<DashboardFilters>('filters', { default: () => ({}) })\nconst messages = useDashboardMessages(() => props.messages)\n\nconst grid = ref<GridLayoutExpose>()\nconst observers = createObservers()\nprovide(observersKey, observers)\nonBeforeUnmount(() => observers.dispose())\n/** This many widgets are drawn in full at once, the rest when they come near the view. */\nconst EAGER = 16\nconst root = ref<HTMLElement>()\n\nconst typeMap = computed(() => new Map(props.widgets.map((type) => [type.type, type])))\nconst typeOf = (widget: DashboardWidget) => typeMap.value.get(widget.type)\nconst widgetMap = computed(() => new Map(model.value.widgets.map((widget) => [widget.id, widget])))\n/** The first widgets of the document, drawn in full without waiting to be seen. */\nconst eagerIds = computed(() => new Set(model.value.widgets.slice(0, EAGER).map((w) => w.id)))\n/** Widgets added once the dashboard is up are drawn in full straight away (not reactive on purpose). */\nlet mounted = false\nonMounted(() => (mounted = true))\nconst isEager = (id: string) => mounted || eagerIds.value.has(id)\nconst titleOf = (widget: DashboardWidget) => widget.title || typeOf(widget)?.label || widget.type\n\nconst tiers = computed(() => gridTiers(props.columns, props.breakpoints))\n/** The widest breakpoint: its places are the main ones. */\nconst main = computed(() => tiers.value[0]!)\nconst mainOptions = computed<GridLayoutOptions>(() => ({\n columns: Math.max(1, Math.round(main.value.columns)),\n compact: props.compact,\n}))\n\n// ---------------------------------------------------------------------------\n// Changes and history\n\nconst history = new DashboardHistory()\n/** Bumped whenever the history changes, so `canUndo` and `canRedo` update. */\nconst historyVersion = ref(0)\n/** The document last emitted, to tell our own changes from the app's. */\nlet emitted: DashboardDocument | undefined\nwatch(model, (doc) => {\n // the app may hand back a reactive copy of what was emitted\n // a document the app put in itself: the old steps no longer fit it\n if (toRaw(doc) === emitted) return\n history.clear()\n historyVersion.value++\n})\n\nfunction commit(tr: DashboardTransaction) {\n emitted = toRaw(tr.doc)\n model.value = tr.doc\n emit('change', tr)\n}\n\n/** A transaction on the document as it is now, to `dispatch`. */\nfunction transaction() {\n return new DashboardTransaction(model.value)\n}\n\n/** Makes the changes of a transaction, as one undo step (or part of one, by `meta.group`). */\nfunction dispatch(tr: DashboardTransaction) {\n if (toRaw(tr.before) !== toRaw(model.value))\n throw new Error(\n '[@dou-ui/dashboard] the transaction was made on another version of the dashboard',\n )\n if (!tr.changed) return\n const shared = props.collaboration\n if (!shared) {\n history.record(tr)\n historyVersion.value++\n }\n commit(tr)\n shared?.transaction(tr)\n}\n\n/** Steps made into a transaction and dispatched, for the dashboard's own changes. */\nfunction run(steps: DashboardStep[], meta: DashboardTransactionMeta) {\n const tr = applySteps(model.value, steps, meta)\n dispatch(tr)\n return tr\n}\n\n// ---------------------------------------------------------------------------\n// Comments on widgets\n\nconst comments = useDashboardComments({\n props,\n emit,\n model,\n run,\n carry: (id, patch) => history.carry(id, patch),\n})\n/** The full screen view, which has a comment button of its own. */\nconst fullscreenBody = shallowRef<HTMLElement | null>(null)\n/** A widget's comment button: in the full screen view while that widget is there. */\nfunction buttonOf(id: string) {\n const selector = `[data-dou-comment=\"${CSS.escape(id)}\"]`\n const inFullscreen = fullscreenId.value === id ? fullscreenBody.value : null\n return (\n inFullscreen?.querySelector<HTMLElement>(selector) ??\n root.value?.querySelector<HTMLElement>(selector) ??\n null\n )\n}\n/** Where a widget's card goes: by its comment button, or by the widget until the button is drawn. */\nconst cardSpotOf = (id: string) =>\n buttonOf(id) ??\n root.value?.querySelector<HTMLElement>(`[data-grid-id=\"${CSS.escape(id)}\"]`) ??\n null\n/** The comment shortcut in the full screen view, which is outside the dashboard. */\nfunction onFullscreenKeydown(event: KeyboardEvent) {\n const id = fullscreenId.value\n if (!id || event.defaultPrevented || !comments.enabled.value || isTyping(event.target)) return\n if (matchesHotkey(event, 'mod+alt+m') && comments.start(id)) event.preventDefault()\n}\n// A thread opened from outside, e.g. a list beside the dashboard: bring its widget into view.\nwatch(\n () => props.activeComment,\n (id) => {\n const widget = id && !comments.openedHere(id) ? comments.widgetOf(id) : null\n if (widget) grid.value?.scrollToItem(widget)\n },\n)\n\nfunction undo() {\n if (props.collaboration) return props.collaboration.undo()\n const tr = history.undo(model.value)\n historyVersion.value++\n if (!tr) return false\n if (tr.changed) commit(tr)\n return true\n}\n\nfunction redo() {\n if (props.collaboration) return props.collaboration.redo()\n const tr = history.redo(model.value)\n historyVersion.value++\n if (!tr) return false\n if (tr.changed) commit(tr)\n return true\n}\n\nconst canUndo = computed(() =>\n props.collaboration ? props.collaboration.canUndo() : (historyVersion.value, history.canUndo),\n)\nconst canRedo = computed(() =>\n props.collaboration ? props.collaboration.canRedo() : (historyVersion.value, history.canRedo),\n)\n\n// ---------------------------------------------------------------------------\n// The grid: widgets as items, places by breakpoint\n\nfunction limitsOf(type: DashboardWidgetType<any> | undefined) {\n const size = type?.size ?? DEFAULT_WIDGET_SIZE\n return {\n w: size.w,\n h: size.h,\n minW: size.minW,\n maxW: size.maxW,\n minH: size.minH,\n maxH: size.maxH,\n }\n}\n\n/** The widgets on the widest breakpoint; those without a place there get the first spot they fit. */\nconst placeOf = (item: DashboardPlace): DashboardPlace => ({\n x: item.x,\n y: item.y,\n w: item.w,\n h: item.h,\n})\n\n// The item of a widget that didn't change is the same object as before, so\n// the grid and its other items have nothing to draw again.\nlet itemCache = new WeakMap<DashboardWidget, GridItem>()\nwatch([typeMap, main], () => (itemCache = new WeakMap()), { flush: 'sync' })\nconst items = computed<GridItem[]>(() => {\n const name = main.value.name\n const options = mainOptions.value\n const placed: GridItem[] = []\n const result = new Map<string, GridItem>()\n const missing: DashboardWidget[] = []\n for (const widget of model.value.widgets) {\n const raw = toRaw(widget)\n const place = raw.place[name]\n if (!place) {\n missing.push(raw)\n continue\n }\n let item = itemCache.get(raw)\n if (!item) {\n const { minW, maxW, minH, maxH } = limitsOf(typeOf(raw))\n item = { id: raw.id, ...placeOf(place), minW, maxW, minH, maxH }\n itemCache.set(raw, item)\n }\n placed.push(item)\n result.set(raw.id, item)\n }\n for (const widget of missing) {\n const fitted = fitItem(\n { id: widget.id, x: 0, y: 0, ...limitsOf(typeOf(widget)) },\n options.columns,\n )\n const item = { ...fitted, ...findPlace(placed, fitted, options) }\n placed.push(item)\n result.set(widget.id, item)\n }\n return model.value.widgets.map((widget) => result.get(widget.id)!)\n})\n\n/** The places on the narrower breakpoints. */\nconst layouts = computed<GridLayouts>(() => {\n const result: GridLayouts = {}\n for (const tier of tiers.value.slice(1)) {\n const list = []\n for (const widget of model.value.widgets) {\n const place = widget.place[tier.name]\n if (place) list.push({ id: widget.id, ...place })\n }\n if (list.length) result[tier.name] = list\n }\n return result\n})\n\n// The grid tells a move with `update:modelValue`, `update:layouts` and\n// `drop` one after another; they are gathered into one change.\nlet pending: { items?: GridItem[]; layouts?: GridLayouts; drop?: { id: string; type: string } } = {}\nlet flushing = false\nfunction gather(part: typeof pending) {\n Object.assign(pending, part)\n if (flushing) return\n flushing = true\n queueMicrotask(flush)\n}\n\nfunction flush() {\n flushing = false\n const { items: list, layouts: saved, drop } = pending\n pending = {}\n const doc = model.value\n const places = new Map<string, DashboardWidget['place']>()\n const setPlace = (id: string, name: string, item: DashboardPlace) => {\n let place = places.get(id)\n if (!place) places.set(id, (place = { ...widgetMap.value.get(id)?.place }))\n place[name] = placeOf(item)\n }\n for (const item of list ?? []) setPlace(item.id, main.value.name, item)\n for (const [name, others] of Object.entries(saved ?? {}))\n for (const item of others ?? []) setPlace(item.id, name, item)\n const steps: DashboardStep[] = []\n for (const [id, place] of places) {\n if (widgetMap.value.has(id)) {\n steps.push({ type: 'update', id, patch: { place } })\n } else if (drop?.id === id) {\n const type = typeMap.value.get(drop.type)\n if (type)\n steps.push({ type: 'add', widget: newWidget(id, type, place), index: doc.widgets.length })\n }\n }\n const tr = run(steps, { origin: drop ? 'drop' : 'layout' })\n if (drop && tr.changed) {\n const widget = widgetMap.value.get(drop.id)\n if (widget) say(format(messages.value.added, { item: titleOf(widget) }))\n }\n}\n\nfunction onDrop(event: GridDropEvent) {\n const data = event.data as { type?: string } | undefined\n if (data?.type) gather({ drop: { id: event.item.id, type: data.type } })\n}\n\nconst dragging = ref(false)\n\n// ---------------------------------------------------------------------------\n// Adding, copying and removing\n\nfunction newId(type: string) {\n const used = widgetMap.value\n for (;;) {\n const id = props.createId?.(type) ?? `${type}-${Math.random().toString(36).slice(2, 8)}`\n if (!used.has(id)) return id\n if (props.createId)\n throw new Error(`[@dou-ui/dashboard] createId gave \"${id}\", which is already in use`)\n }\n}\n\nfunction newWidget(\n id: string,\n type: DashboardWidgetType<any>,\n place: DashboardWidget['place'],\n options: Pick<DashboardAddOptions, 'title' | 'props'> = {},\n): DashboardWidget {\n const widget: DashboardWidget = { id, type: type.type, place }\n if (options.title) widget.title = options.title\n const initial = defaultPropsOf(type)\n if (initial || options.props) widget.props = { ...initial, ...options.props }\n return widget\n}\n\n/**\n * Puts a new item on the widest breakpoint, at `at` (pushing the others\n * away) or in the first spot it fits; the steps also move any widget it\n * pushed.\n */\nfunction placeNew(item: GridItem, at: Pick<DashboardPlace, 'x' | 'y'> | undefined) {\n const options = mainOptions.value\n const fitted = fitItem(item, options.columns)\n const spot = at ?? findPlace(items.value, fitted, options)\n const layout = moveItem([...items.value, { ...fitted, ...spot }], item.id, spot, options)\n const steps: DashboardStep[] = []\n let place: DashboardPlace = { ...fitted, ...spot }\n for (const next of layout) {\n if (next.id === item.id) {\n place = placeOf(next)\n continue\n }\n const widget = widgetMap.value.get(next.id)\n const old = widget && items.value.find((other) => other.id === next.id)\n if (widget && old && (old.x !== next.x || old.y !== next.y))\n steps.push({\n type: 'update',\n id: next.id,\n patch: { place: { ...widget.place, [main.value.name]: placeOf(next) } },\n })\n }\n return { place, steps }\n}\n\nfunction addWidget(typeName: string, options: DashboardAddOptions = {}) {\n const type = typeMap.value.get(typeName)\n if (!type) return undefined\n const id = newId(typeName)\n const { place, steps } = placeNew({ id, x: 0, y: 0, ...limitsOf(type) }, options.at)\n const widget = newWidget(id, type, { [main.value.name]: place }, options)\n run([{ type: 'add', widget, index: model.value.widgets.length }, ...steps], {\n origin: 'api',\n })\n say(format(messages.value.added, { item: titleOf(widget) }))\n if (options.focus !== false) void nextTick(() => focusWidget(id))\n return id\n}\n\n/** Adds a copy of a widget (from this dashboard or the clipboard) beside `near`, or in the first free spot. */\nfunction addCopy(source: DashboardWidget, near?: GridItem) {\n const type = typeOf(source)\n const id = newId(source.type)\n const columns = mainOptions.value.columns\n const size = near ?? { ...limitsOf(type), ...source.place[main.value.name] }\n const at = near\n ? near.x + near.w * 2 <= columns\n ? { x: near.x + near.w, y: near.y }\n : { x: near.x, y: near.y + near.h }\n : undefined\n const { place, steps } = placeNew({ ...limitsOf(type), id, x: 0, y: 0, w: size.w, h: size.h }, at)\n const widget: DashboardWidget = {\n ...cloneJson(source),\n id,\n place: { [main.value.name]: place },\n }\n // a copy starts without the original's discussions\n delete widget.comments\n const index = near\n ? model.value.widgets.findIndex((other) => other.id === near.id) + 1\n : model.value.widgets.length\n run([{ type: 'add', widget, index }, ...steps], { origin: 'copy' })\n say(format(messages.value.added, { item: titleOf(widget) }))\n void nextTick(() => focusWidget(id))\n return id\n}\n\nfunction duplicateWidget(id: string) {\n const widget = widgetMap.value.get(id)\n const item = items.value.find((other) => other.id === id)\n return widget && item ? addCopy(widget, item) : undefined\n}\n\nfunction removeWidget(id: string) {\n const list = model.value.widgets\n const index = list.findIndex((widget) => widget.id === id)\n if (index < 0) return\n const widget = list[index]!\n const hadFocus = root.value?.contains(document.activeElement)\n const neighbour = list[index + 1] ?? list[index - 1]\n if (settingsId.value === id) settingsId.value = undefined\n if (fullscreenId.value === id) fullscreenId.value = undefined\n run([{ type: 'remove', id }], { origin: 'api' })\n say(format(messages.value.removed, { item: titleOf(widget) }))\n if (hadFocus) void nextTick(() => (neighbour ? focusWidget(neighbour.id) : root.value?.focus()))\n}\n\nfunction focusWidget(id: string) {\n grid.value?.focusItem(id)\n}\n\n/** The widget being dragged in, which has no widget yet: its name is its type's. */\nlet adding: { id: string; label: string } | undefined\n\nfunction startAdd(event: PointerEvent, typeName: string) {\n const type = typeMap.value.get(typeName)\n if (!type || !props.editable) return\n adding = { id: newId(typeName), label: type.label }\n grid.value?.startDrop(event, { id: adding.id, ...limitsOf(type) }, { type: typeName })\n}\n\nfunction labelOf(item: GridItem) {\n const widget = widgetMap.value.get(item.id)\n return widget ? titleOf(widget) : item.id === adding?.id ? adding.label : item.id\n}\n\nfunction setProps(id: string, patch: Record<string, unknown>, options: { group?: string } = {}) {\n const widget = widgetMap.value.get(id)\n if (!widget) return\n run([{ type: 'update', id, patch: { props: { ...widget.props, ...patch } } }], {\n origin: 'widget',\n group: options.group,\n })\n}\n\nfunction rename(id: string, title: string) {\n run([{ type: 'update', id, patch: { title: title.trim() || undefined } }], {\n origin: 'title',\n })\n}\n\n// ---------------------------------------------------------------------------\n// Settings and full screen\n\nconst settingsId = ref<string>()\nconst settingsWidget = computed(() =>\n settingsId.value ? widgetMap.value.get(settingsId.value) : undefined,\n)\nconst fullscreenId = ref<string>()\nconst fullscreenWidget = computed(() =>\n fullscreenId.value ? widgetMap.value.get(fullscreenId.value) : undefined,\n)\n/** The widget to give focus back to when a drawer or dialog closes. */\nlet returnTo: string | undefined\n\nfunction openSettings(id: string) {\n if (!props.editable || !widgetMap.value.has(id)) return\n returnTo = id\n settingsId.value = id\n}\n\nfunction openFullscreen(id: string) {\n const widget = widgetMap.value.get(id)\n if (!widget || typeOf(widget)?.fullscreen === false) return\n returnTo = id\n fullscreenId.value = id\n}\n\nfunction restoreFocus() {\n const id = returnTo\n returnTo = undefined\n if (id && widgetMap.value.has(id)) focusWidget(id)\n}\n\nwatch(\n () => props.editable,\n (editable) => {\n if (!editable) settingsId.value = undefined\n },\n)\n\nfunction onAction(id: string, action: WidgetAction) {\n if (action === 'settings') openSettings(id)\n else if (action === 'fullscreen') openFullscreen(id)\n else if (action === 'duplicate') duplicateWidget(id)\n else removeWidget(id)\n}\n\n// ---------------------------------------------------------------------------\n// Editing together\n\n/** Changes from others wait while a widget is dragged here; then the shared document is read again. */\nlet held = false\n\nfunction applyShared(doc: DashboardDocument, origin: 'remote' | 'undo' | 'redo') {\n if (dragging.value) {\n held = true\n return\n }\n const tr = replaceWidgets(new DashboardTransaction(model.value), doc, { keepComments: false })\n if (!tr.changed) return\n tr.setMeta('history', false).setMeta('origin', origin)\n const ids = new Set(tr.doc.widgets.map((widget) => widget.id))\n if (settingsId.value && !ids.has(settingsId.value)) settingsId.value = undefined\n if (fullscreenId.value && !ids.has(fullscreenId.value)) fullscreenId.value = undefined\n commit(tr)\n}\n\n// one written in the template is new on every render: each one starts over and clears undo\nlet setupTimes: number[] = []\nlet setupWarned = false\nfunction warnRepeatedSetup() {\n if (process.env.NODE_ENV === 'production' || setupWarned) return\n const now = Date.now()\n setupTimes = [...setupTimes.filter((time) => now - time < 1000), now]\n if (setupTimes.length < 4) return\n setupWarned = true\n console.warn(\n '[@dou-ui/dashboard] collaboration keeps being replaced, which starts it over and clears undo each time. Create it once in <script setup> instead of in the template.',\n )\n}\n\nwatch(\n () => props.collaboration,\n (shared, _, onCleanup) => {\n if (shared) warnRepeatedSetup()\n history.clear()\n historyVersion.value++\n if (!shared) return\n const stop = shared.setup({ document: () => model.value, apply: applyShared })\n onCleanup(() => {\n shared.presence?.(null)\n stop()\n })\n },\n { immediate: true },\n)\n\n/** The widget being moved or resized here, and how. */\nconst drag = shallowRef<GridDragEvent>()\nfunction onDragStart(event: GridDragEvent) {\n dragging.value = true\n drag.value = event\n}\nfunction onDragEnd() {\n dragging.value = false\n drag.value = undefined\n if (!held) return\n held = false\n // after the drop's own change, which the grid hands over in a microtask\n queueMicrotask(() => queueMicrotask(() => props.collaboration?.sync()))\n}\n\n/** The widget whose item, or a control in it, has focus. */\nconst focused = ref<string>()\nfunction onFocusIn(event: FocusEvent) {\n const item = (event.target as HTMLElement).closest?.<HTMLElement>('[data-grid-id]')\n focused.value = item?.closest('.dou-dashboard') === root.value ? item?.dataset.gridId : undefined\n}\nfunction onFocusOut(event: FocusEvent) {\n if (!root.value?.contains(event.relatedTarget as Node | null)) focused.value = undefined\n}\n\n/** What this person is doing, shown to the others. */\nconst presence = computed<DashboardPresence | null>(() => {\n const moving = drag.value\n if (moving && moving.kind !== 'drop' && widgetMap.value.has(moving.id))\n return { widget: moving.id, action: moving.kind as DashboardPresenceAction }\n if (settingsId.value) return { widget: settingsId.value, action: 'settings' }\n if (focused.value && widgetMap.value.has(focused.value))\n return { widget: focused.value, action: 'focus' }\n return null\n})\nwatch(\n [presence, () => props.collaboration] as const,\n ([current, shared], previous) => {\n if (!shared?.presence) return\n const [before, sharedBefore] = previous ?? []\n if (\n sharedBefore === shared &&\n before?.widget === current?.widget &&\n before?.action === current?.action\n )\n return\n shared.presence(current)\n },\n { immediate: true },\n)\nconst NO_PEERS: readonly DashboardPeer[] = []\n\nprovide(frameContextKey, {\n comments,\n widget: (id) => widgetMap.value.get(id),\n type: typeOf,\n title: titleOf,\n editing: () => props.editable,\n messages: () => messages.value,\n eager: isEager,\n all: () => showAll.value,\n setProps,\n action: onAction,\n rename,\n linked: (id) => linksByWidget.value.get(id) ?? NO_LINKS,\n clearFilter: (key) => setFilter(undefined, key, undefined),\n peers: (id) => props.collaboration?.peers?.(id) ?? NO_PEERS,\n setWorkbook,\n exporters: (id) => (workbooks.has(id) ? exporterList.value : NO_EXPORTERS),\n exportTo: exportFromMenu,\n exporting: (id) => exporting.has(id),\n setDrawn: (id, drawn) => (drawn ? drawnIds.add(id) : drawnIds.delete(id)),\n exportError: (id) => exportErrors.get(id),\n clearExportError: (id) => exportErrors.delete(id),\n reportError: (id, error) => emit('widgetError', { widget: id, part: 'content', error }),\n})\n\n// ---------------------------------------------------------------------------\n// Exporting a widget's content as a file\n\n/** How each mounted widget gives its content as a workbook; see `setWorkbook`. */\nconst workbooks = shallowReactive(new Map<string, () => SheetWorkbook | undefined>())\nfunction setWorkbook(id: string, get: () => SheetWorkbook | undefined) {\n workbooks.set(id, get)\n return () => {\n if (workbooks.get(id) === get) workbooks.delete(id)\n }\n}\n\n// the file code comes with the first exporter, in a chunk of its own\ntype SheetCore = typeof import('@dou-ui/sheet/core')\nconst sheetCore = shallowRef<SheetCore>()\nlet loadingCore: Promise<SheetCore> | undefined\nfunction loadSheetCore() {\n loadingCore ??= import('@dou-ui/sheet/core').then((core) => (sheetCore.value = core))\n return loadingCore\n}\nwatch(\n () => props.exporters?.length,\n (count) => count && void loadSheetCore(),\n { immediate: true },\n)\n\nconst NO_EXPORTERS: FrameExporter[] = []\nconst { locale } = useLocale()\n/** The exporters of the menu, without those that draw a sheet's grid. */\nconst exporterList = computed<FrameExporter[]>(() => {\n const core = sheetCore.value\n if (!core) return NO_EXPORTERS\n const sheetMessages = core.sheetMessagesFor(locale.value.name)\n return (props.exporters ?? [])\n .filter((exporter) => exporter.needs !== 'view')\n .map((exporter) => ({\n name: exporter.name,\n label: typeof exporter.label === 'function' ? exporter.label(sheetMessages) : exporter.label,\n icon: exporter.icon ?? fileIconOf(exporter.extension),\n }))\n})\n\nfunction exporterOf(exporter: string | SheetExporter) {\n if (typeof exporter !== 'string') return exporter\n const found = props.exporters?.find((item) => item.name === exporter)\n if (!found) throw new RangeError(`[@dou-ui/dashboard] there is no exporter \"${exporter}\"`)\n return found\n}\n\n/** A widget's file and sheet name: its title, or its type's name. */\nconst exportNameOf = (widget: DashboardWidget) => titleOf(widget).trim() || widget.type\n\n/** Why something failed, as people read it: a file error's `reason`, or its message. */\nfunction reasonOf(error: unknown) {\n if (error instanceof Error)\n return 'reason' in error && typeof error.reason === 'string' ? error.reason : error.message\n return String(error)\n}\n\n/**\n * A widget's content as a file: its workbook written by the exporter, a\n * name from `exporters` or an exporter itself. Waits for the data shown,\n * and draws the widget first when it is far from the view.\n */\nasync function exportWidget(\n id: string,\n exporter: string | SheetExporter,\n options: DashboardExportOptions = {},\n): Promise<Blob> {\n const chosen = exporterOf(exporter)\n const widget = widgetMap.value.get(id)\n if (!widget) throw new RangeError(`[@dou-ui/dashboard] there is no widget \"${id}\"`)\n const core = await loadSheetCore()\n // a widget drawn already that gives no workbook never will: no need to draw them all\n await settle(5000, !drawnIds.has(id))\n options.signal?.throwIfAborted()\n const get = workbooks.get(id)\n const book = get?.()\n if (!book)\n throw new core.SheetFileError(\n get\n ? `[@dou-ui/dashboard] the widget \"${id}\" has no data to export now`\n : `[@dou-ui/dashboard] the widget \"${id}\" doesn't give its content as a workbook (setWorkbook)`,\n 'empty',\n messages.value.nothingToExport,\n )\n // sheets without a name take the widget's, as the file does\n const name = exportNameOf(widget)\n const named = book.sheets.some((sheet) => !sheet.name)\n ? { ...book, sheets: book.sheets.map((sheet) => (sheet.name ? sheet : { ...sheet, name })) }\n : book\n return core.exportWorkbook(named, chosen, {\n options: options.options,\n signal: options.signal,\n locale: locale.value.name,\n })\n}\n\n/** The widgets drawn in full; the others show only their title until they come near. */\nconst drawnIds = new Set<string>()\n\n/** Why the last export from each widget's menu failed, shown over its content until closed. */\nconst exportErrors = shallowReactive(new Map<string, string>())\n/** The widgets exporting now, whose export button waits. */\nconst exporting = shallowReactive(new Set<string>())\n\n/**\n * Exports a widget's content and saves the file in the downloads, as its\n * menu does. A failure shows over the widget and comes as `exportError`,\n * and the promise rejects.\n */\nasync function saveWidget(\n id: string,\n exporter: string | SheetExporter,\n options: DashboardSaveOptions = {},\n): Promise<void> {\n const chosen = exporterOf(exporter)\n const widget = widgetMap.value.get(id)\n if (!widget) throw new RangeError(`[@dou-ui/dashboard] there is no widget \"${id}\"`)\n const name = options.fileName ?? `${exportNameOf(widget)}.${chosen.extension}`\n exportErrors.delete(id)\n exporting.add(id)\n let blob: Blob\n try {\n blob = await exportWidget(id, chosen, options)\n } catch (error) {\n const event: DashboardExportErrorEvent = {\n widget: id,\n exporter: chosen.name,\n error,\n message: format(messages.value.exportFailed, { name, reason: reasonOf(error) }),\n defaultPrevented: false,\n preventDefault() {\n this.defaultPrevented = true\n },\n }\n emit('exportError', event)\n // the notice is read out itself; told by the app, it is only read out\n if (event.defaultPrevented) say(event.message)\n else exportErrors.set(id, event.message)\n throw error\n } finally {\n exporting.delete(id)\n }\n downloadBlob(blob, name)\n say(format(messages.value.exported, { name }))\n}\n\nfunction exportFromMenu(id: string, name: string) {\n if (exporting.has(id)) return\n // the failure is shown over the widget\n saveWidget(id, name).catch(() => {})\n}\n\n// ---------------------------------------------------------------------------\n// Data and filters\n\nconst store = createSourceStore(() => props.sources)\nonBeforeUnmount(() => store.dispose())\n// a source replaced by one that loads differently loads again\nwatch(\n () => props.sources,\n (next, previous) => {\n const names = new Set([...Object.keys(previous ?? {}), ...Object.keys(next ?? {})])\n for (const name of names) if (!sameSource(previous?.[name], next?.[name])) store.reload(name)\n },\n)\n\n/** The filters set by widgets, by key: they apply to every widget but the one that set them. */\nconst links = shallowRef(new Map<string, DashboardLinkedFilter>())\nconst EMPTY_SET: ReadonlySet<string> = new Set()\nconst NO_LINKS: DashboardLinkedFilter[] = []\nconst linksByWidget = computed(() => {\n const result = new Map<string, DashboardLinkedFilter[]>()\n for (const link of links.value.values()) {\n const list = result.get(link.widget)\n if (list) list.push(link)\n else result.set(link.widget, [link])\n }\n return result\n})\nconst emittedByWidget = computed(() => {\n const result = new Map<string, Set<string>>()\n for (const [id, list] of linksByWidget.value)\n result.set(id, new Set(list.map((link) => link.key)))\n return result\n})\n\n// The values as set last: with `v-model:filters` the model only updates once\n// the parent draws again, so several filters set at once build on this.\nconst values = shallowRef<DashboardFilters>(filters.value)\nwatch(filters, (next) => (values.value = next))\n\nfunction setFilter(\n widget: string | undefined,\n key: string,\n value: unknown,\n options: DashboardSetFilterOptions = {},\n) {\n const next = { ...values.value }\n const nextLinks = new Map(links.value)\n if (isEmptyFilter(value)) delete next[key]\n else next[key] = value\n if (widget && !isEmptyFilter(value) && widgetMap.value.has(widget))\n nextLinks.set(key, { key, value, label: options.label ?? filterText(value), widget })\n else nextLinks.delete(key)\n links.value = nextLinks\n values.value = next\n filters.value = next\n}\n\nfunction clearFilters() {\n links.value = new Map()\n values.value = {}\n filters.value = {}\n}\n\n// a filter changed from outside (the filter bar, the app) is no longer the widget's\nwatch(filters, (current) => {\n let next: Map<string, DashboardLinkedFilter> | undefined\n for (const [key, link] of links.value) {\n if (sameValue(current[key], link.value)) continue\n next ??= new Map(links.value)\n next.delete(key)\n }\n if (next) links.value = next\n})\n\n// the filters of a widget that is removed go with it\nwatch(widgetMap, (map) => {\n for (const link of links.value.values())\n if (!map.has(link.widget)) setFilter(undefined, link.key, undefined)\n})\n\nconst sourceNames = computed(() => Object.keys(props.sources ?? {}))\nprovide(dataContextKey, {\n store,\n sourceNames: () => sourceNames.value,\n filters: () => values.value,\n emitted: (id) => emittedByWidget.value.get(id) ?? EMPTY_SET,\n linked: (id) => linksByWidget.value.get(id) ?? NO_LINKS,\n setFilter,\n})\n\n// ---------------------------------------------------------------------------\n// Keyboard and clipboard\n\nconst CLIPBOARD_KIND = 'dou-dashboard/widget'\n\nfunction isTyping(target: EventTarget | null) {\n const el = target as HTMLElement | null\n if (!el?.tagName) return false\n return (\n el.isContentEditable ||\n el.tagName === 'TEXTAREA' ||\n el.tagName === 'SELECT' ||\n (el.tagName === 'INPUT' &&\n !['checkbox', 'radio', 'button', 'range', 'color'].includes((el as HTMLInputElement).type))\n )\n}\n\n/** The widget an element is in, such as the one holding the focus. */\nfunction widgetHolding(target: EventTarget | null) {\n const el = (target as HTMLElement | null)?.closest?.<HTMLElement>('[data-grid-id]')\n return el && el.closest('.dou-dashboard') === root.value ? el.dataset.gridId : undefined\n}\n\n/** The widget whose item itself has focus (not a control inside it). */\nfunction focusedId(target: EventTarget | null) {\n const el = target as HTMLElement | null\n return el?.dataset?.gridId && el.closest('.dou-dashboard') === root.value\n ? el.dataset.gridId\n : undefined\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.defaultPrevented || isTyping(event.target)) return\n // anywhere in a widget, also while viewing: its discussions\n if (matchesHotkey(event, 'mod+alt+m') && comments.enabled.value) {\n const id = widgetHolding(event.target)\n if (id && comments.start(id)) event.preventDefault()\n return\n }\n if (!props.editable) return\n if (matchesHotkey(event, 'mod+z')) {\n event.preventDefault()\n undo()\n return\n }\n if (matchesHotkey(event, 'mod+shift+z') || matchesHotkey(event, 'mod+y')) {\n event.preventDefault()\n redo()\n return\n }\n const id = focusedId(event.target)\n if (!id || dragging.value) return\n if (event.key === 'Delete' || event.key === 'Backspace') {\n event.preventDefault()\n removeWidget(id)\n } else if (matchesHotkey(event, 'mod+d')) {\n event.preventDefault()\n duplicateWidget(id)\n }\n}\n\nfunction onCopy(event: ClipboardEvent, cut = false) {\n if (isTyping(event.target)) return\n const id = focusedId(event.target)\n const widget = id && widgetMap.value.get(id)\n if (!widget || !event.clipboardData) return\n event.clipboardData.setData('text/plain', JSON.stringify({ kind: CLIPBOARD_KIND, widget }))\n event.preventDefault()\n if (cut && props.editable) removeWidget(widget.id)\n}\n\nfunction onPaste(event: ClipboardEvent) {\n if (!props.editable || isTyping(event.target)) return\n let data: { kind?: string; widget?: DashboardWidget } | undefined\n try {\n data = JSON.parse(event.clipboardData?.getData('text/plain') ?? '')\n } catch {\n return\n }\n const widget = data?.kind === CLIPBOARD_KIND ? data.widget : undefined\n if (!widget?.type || !typeMap.value.has(widget.type)) return\n event.preventDefault()\n const id = focusedId(event.target)\n addCopy(widget, id ? items.value.find((item) => item.id === id) : undefined)\n}\n\n// ---------------------------------------------------------------------------\n// Export and print\n\n/** Every widget drawn in full, not only those near the view. */\nconst showAll = ref(false)\nconst nextFrame = () => new Promise<void>((resolve) => requestAnimationFrame(() => resolve()))\n\n/** Draws every widget (unless `all` is false) and waits for their sources (at most `timeout` ms) and one more frame. */\nasync function settle(timeout = 5000, all = true) {\n if (all) showAll.value = true\n await nextTick()\n const start = performance.now()\n await nextFrame()\n while (store.busy() && performance.now() - start < timeout) await nextFrame()\n await nextFrame()\n await nextFrame()\n}\n\n/** The dashboard as it is shown, every widget drawn, as a PNG image. */\nasync function exportPNG(options?: DashboardImageOptions) {\n await settle()\n const el = root.value!\n // the controls are left out; the copy is made before the class goes\n el.classList.add('is-exporting')\n try {\n return elementToPNG(el, options)\n } finally {\n el.classList.remove('is-exporting')\n }\n}\n\n/** Draws every widget, waits for their data, and opens the browser's print dialog. */\nasync function print() {\n await settle()\n window.print()\n}\n\n// printing from the browser's menu: draw what is far from the view, as far as there is time\nfunction onBeforePrint() {\n showAll.value = true\n}\nonMounted(() => window.addEventListener('beforeprint', onBeforePrint))\nonBeforeUnmount(() => window.removeEventListener('beforeprint', onBeforePrint))\n\n// ---------------------------------------------------------------------------\n// Announcements\n\nconst live = ref('')\nfunction say(text: string) {\n live.value = ''\n requestAnimationFrame(() => (live.value = text))\n}\n\nconst breakpointName = shallowRef(main.value.name)\nfunction onBreakpoint(name: string) {\n breakpointName.value = name\n emit('breakpointChange', name)\n}\n\nconst minHeight = computed(() => {\n const gap = Array.isArray(props.gap) ? props.gap[1] : props.gap\n return `${props.rowHeight * 3 + gap * 2}px`\n})\n\ndefineExpose<DashboardExpose>({\n transaction,\n dispatch,\n undo,\n redo,\n get canUndo() {\n return canUndo.value\n },\n get canRedo() {\n return canRedo.value\n },\n addWidget,\n removeWidget,\n comment: (id = widgetHolding(document.activeElement)) => !!id && comments.start(id),\n getCommentAnchors: comments.anchors,\n duplicateWidget,\n focusWidget,\n openSettings,\n openFullscreen,\n startAdd,\n get types() {\n return props.widgets\n },\n get breakpoint() {\n return grid.value?.breakpoint ?? breakpointName.value\n },\n get editable() {\n return props.editable\n },\n get document() {\n return model.value\n },\n get filters() {\n return values.value\n },\n get linkedFilters() {\n return [...links.value.values()]\n },\n setFilter: (key, value, options) => setFilter(undefined, key, value, options),\n clearFilters,\n refresh: (source) => store.reload(source),\n exportPNG,\n exportWidget: exportWidget as DashboardExpose['exportWidget'],\n saveWidget: saveWidget as DashboardExpose['saveWidget'],\n print,\n})\n</script>\n\n<template>\n <div\n ref=\"root\"\n :class=\"['dou-dashboard', editable && 'is-editable']\"\n role=\"region\"\n :aria-label=\"label || messages.dashboard\"\n tabindex=\"-1\"\n @keydown=\"onKeydown\"\n @copy=\"onCopy($event)\"\n @cut=\"onCopy($event, true)\"\n @paste=\"onPaste\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <GridLayout\n ref=\"grid\"\n :model-value=\"items\"\n :layouts=\"layouts\"\n :columns=\"columns\"\n :breakpoints=\"breakpoints\"\n :row-height=\"rowHeight\"\n :gap=\"gap\"\n :compact=\"compact\"\n :editable=\"editable\"\n :handle=\"true\"\n :item-label=\"labelOf\"\n :messages=\"messages\"\n :style=\"editable && !model.widgets.length ? { minHeight } : undefined\"\n @update:model-value=\"(list) => gather({ items: list })\"\n @update:layouts=\"(next) => gather({ layouts: next })\"\n @drop=\"onDrop\"\n @drag-start=\"onDragStart\"\n @drag-end=\"onDragEnd\"\n @breakpoint-change=\"onBreakpoint\"\n >\n <template #item=\"{ item }\">\n <WidgetFrame :id=\"item.id\" />\n </template>\n </GridLayout>\n <div v-if=\"editable && !model.widgets.length\" class=\"dou-dashboard__empty\">\n <DouEmpty size=\"sm\" :title=\"messages.emptyTitle\" :description=\"messages.emptyHint\">\n <template #icon><LayoutGridPlusIcon /></template>\n </DouEmpty>\n </div>\n <div class=\"dou-dashboard__live\" aria-live=\"polite\" aria-atomic=\"true\">{{ live }}</div>\n <!-- in fullscreen the card goes in the dialog, which keeps the focus to itself -->\n <CommentCard\n v-if=\"comments.enabled.value && !fullscreenWidget\"\n :comments=\"comments\"\n :messages=\"messages\"\n :button-of=\"buttonOf\"\n :place-of=\"cardSpotOf\"\n />\n <DouDrawer\n :open=\"!!settingsWidget\"\n :title=\"\n settingsWidget ? format(messages.settingsTitle, { title: titleOf(settingsWidget) }) : ''\n \"\n :size=\"360\"\n @update:open=\"(open) => !open && (settingsId = undefined)\"\n @closed=\"restoreFocus\"\n >\n <div v-if=\"settingsWidget\" class=\"dou-dashboard__settings\">\n <label class=\"dou-dashboard__field\">\n <span class=\"dou-dashboard__field-label\">{{ messages.titleField }}</span>\n <DouInput\n :model-value=\"settingsWidget.title ?? ''\"\n :placeholder=\"typeOf(settingsWidget)?.label ?? settingsWidget.type\"\n @update:model-value=\"\n (value) =>\n run(\n [\n {\n type: 'update',\n id: settingsWidget!.id,\n patch: { title: value || undefined },\n },\n ],\n { origin: 'settings', group: `title:${settingsWidget!.id}` },\n )\n \"\n />\n </label>\n <WidgetSettings\n v-if=\"typeOf(settingsWidget)?.settings\"\n :widget=\"settingsWidget\"\n :type=\"typeOf(settingsWidget)!\"\n :messages=\"messages\"\n :set-props=\"setProps\"\n :report-error=\"\n (id, error) => emit('widgetError', { widget: id, part: 'settings', error })\n \"\n @update:props=\"\n (value) =>\n run([{ type: 'update', id: settingsWidget!.id, patch: { props: value } }], {\n origin: 'settings',\n group: `props:${settingsWidget!.id}`,\n })\n \"\n />\n </div>\n </DouDrawer>\n <DouDialog\n :open=\"!!fullscreenWidget\"\n :title=\"fullscreenWidget ? titleOf(fullscreenWidget) : ''\"\n fullscreen\n @update:open=\"(open) => !open && (fullscreenId = undefined)\"\n @closed=\"restoreFocus\"\n @keydown=\"onFullscreenKeydown\"\n >\n <div v-if=\"fullscreenWidget\" ref=\"fullscreenBody\" class=\"dou-dashboard__fullscreen\">\n <div v-if=\"comments.enabled.value\" class=\"dou-dashboard__fullscreen-bar\">\n <CommentButton\n :id=\"fullscreenWidget.id\"\n :comments=\"comments\"\n :messages=\"messages\"\n tooltip-placement=\"left\"\n />\n </div>\n <WidgetBody\n class=\"is-fullscreen\"\n :widget=\"fullscreenWidget\"\n :type=\"typeOf(fullscreenWidget)\"\n :editing=\"false\"\n fullscreen\n :messages=\"messages\"\n :set-props=\"setProps\"\n :report-error=\"(id, error) => emit('widgetError', { widget: id, part: 'content', error })\"\n />\n <CommentCard\n v-if=\"comments.enabled.value\"\n :comments=\"comments\"\n :messages=\"messages\"\n :button-of=\"buttonOf\"\n :place-of=\"cardSpotOf\"\n />\n </div>\n </DouDialog>\n </div>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { nextTick, onErrorCaptured, ref, watch } from "vue";
2
+ // src/dashboard/error-boundary.ts
3
+ var REFERENCE = "https://vuejs.org/error-reference/#runtime-";
4
+ /** Setting up, rendering, updating, and the hooks around mounting: the part's view is broken. */
5
+ var VIEW_CODES = /* @__PURE__ */ new Set([
6
+ "sp",
7
+ "bc",
8
+ "c",
9
+ "bm",
10
+ "m",
11
+ "bu",
12
+ "u",
13
+ "0",
14
+ "1",
15
+ "13",
16
+ "15"
17
+ ]);
18
+ var VIEW_NAMES = /* @__PURE__ */ new Set([
19
+ "serverPrefetch hook",
20
+ "beforeCreate hook",
21
+ "created hook",
22
+ "beforeMount hook",
23
+ "mounted hook",
24
+ "beforeUpdate hook",
25
+ "updated",
26
+ "setup function",
27
+ "render function",
28
+ "async component loader",
29
+ "component update"
30
+ ]);
31
+ /**
32
+ * Whether an error broke what a component shows. Errors in event handlers,
33
+ * watchers and the like leave it showing: they go on to the app as usual.
34
+ */
35
+ function breaksView(info) {
36
+ return info.startsWith(REFERENCE) ? VIEW_CODES.has(info.slice(43)) : VIEW_NAMES.has(info);
37
+ }
38
+ /**
39
+ * Keeps an error that breaks a widget's content or settings in that part:
40
+ * `failed` turns on, `report` is told, and the error goes no further. The
41
+ * caller shows the error state and turns `failed` off to try again.
42
+ */
43
+ function useErrorBoundary(label, report) {
44
+ const failed = ref(false);
45
+ let last;
46
+ watch(failed, (on) => {
47
+ if (!on) nextTick(() => !failed.value && (last = void 0));
48
+ });
49
+ onErrorCaptured((error, _instance, info) => {
50
+ if (!breaksView(info)) return;
51
+ failed.value = true;
52
+ const key = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
53
+ if (key !== last) {
54
+ last = key;
55
+ if (process.env.NODE_ENV !== "production") console.error(`[@dou-ui/dashboard] ${label()} failed:`, error);
56
+ report(error);
57
+ }
58
+ return false;
59
+ });
60
+ return failed;
61
+ }
62
+ export { breaksView, useErrorBoundary };
63
+
64
+ //# sourceMappingURL=error-boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-boundary.js","names":[],"sources":["../../src/dashboard/error-boundary.ts"],"sourcesContent":["import { nextTick, onErrorCaptured, ref, watch } from 'vue'\nimport type { Ref } from 'vue'\n\n// Vue names where an error came from: in words while developing, as a code in\n// production (`https://vuejs.org/error-reference/#runtime-<code>`)\nconst REFERENCE = 'https://vuejs.org/error-reference/#runtime-'\n/** Setting up, rendering, updating, and the hooks around mounting: the part's view is broken. */\nconst VIEW_CODES = new Set(['sp', 'bc', 'c', 'bm', 'm', 'bu', 'u', '0', '1', '13', '15'])\nconst VIEW_NAMES = new Set([\n 'serverPrefetch hook',\n 'beforeCreate hook',\n 'created hook',\n 'beforeMount hook',\n 'mounted hook',\n 'beforeUpdate hook',\n 'updated',\n 'setup function',\n 'render function',\n 'async component loader',\n 'component update',\n])\n\n/**\n * Whether an error broke what a component shows. Errors in event handlers,\n * watchers and the like leave it showing: they go on to the app as usual.\n */\nexport function breaksView(info: string): boolean {\n return info.startsWith(REFERENCE)\n ? VIEW_CODES.has(info.slice(REFERENCE.length))\n : VIEW_NAMES.has(info)\n}\n\n/**\n * Keeps an error that breaks a widget's content or settings in that part:\n * `failed` turns on, `report` is told, and the error goes no further. The\n * caller shows the error state and turns `failed` off to try again.\n */\nexport function useErrorBoundary(\n label: () => string,\n report: (error: unknown) => void,\n): Ref<boolean> {\n const failed = ref(false)\n // trying again often fails the same way, e.g. when the first data arrives:\n // that is told once, until the part shows again\n let last: string | undefined\n watch(failed, (on) => {\n if (!on) nextTick(() => !failed.value && (last = undefined))\n })\n onErrorCaptured((error, _instance, info) => {\n if (!breaksView(info)) return\n failed.value = true\n const key = error instanceof Error ? `${error.name}: ${error.message}` : String(error)\n if (key !== last) {\n last = key\n if (process.env.NODE_ENV !== 'production')\n console.error(`[@dou-ui/dashboard] ${label()} failed:`, error)\n report(error)\n }\n return false\n })\n return failed\n}\n"],"mappings":";;AAKA,IAAM,YAAY;;AAElB,IAAM,6BAAa,IAAI,IAAI;CAAC;CAAM;CAAM;CAAK;CAAM;CAAK;CAAM;CAAK;CAAK;CAAK;CAAM;AAAI,CAAC;AACxF,IAAM,6BAAa,IAAI,IAAI;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;AAMD,SAAgB,WAAW,MAAuB;CAChD,OAAO,KAAK,WAAW,SAAS,IAC5B,WAAW,IAAI,KAAK,MAAM,EAAgB,CAAC,IAC3C,WAAW,IAAI,IAAI;AACzB;;;;;;AAOA,SAAgB,iBACd,OACA,QACc;CACd,MAAM,SAAS,IAAI,KAAK;CAGxB,IAAI;CACJ,MAAM,SAAS,OAAO;EACpB,IAAI,CAAC,IAAI,eAAe,CAAC,OAAO,UAAU,OAAO,KAAA,EAAU;CAC7D,CAAC;CACD,iBAAiB,OAAO,WAAW,SAAS;EAC1C,IAAI,CAAC,WAAW,IAAI,GAAG;EACvB,OAAO,QAAQ;EACf,MAAM,MAAM,iBAAiB,QAAQ,GAAG,MAAM,KAAK,IAAI,MAAM,YAAY,OAAO,KAAK;EACrF,IAAI,QAAQ,MAAM;GAChB,OAAO;GACP,IAAA,QAAA,IAAA,aAA6B,cAC3B,QAAQ,MAAM,uBAAuB,MAAM,EAAE,WAAW,KAAK;GAC/D,OAAO,KAAK;EACd;EACA,OAAO;CACT,CAAC;CACD,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"frame-context.js","names":[],"sources":["../../src/dashboard/frame-context.ts"],"sourcesContent":["import type { SheetWorkbook } from '@dou-ui/sheet/core'\nimport type { Component, InjectionKey } from 'vue'\nimport type { DashboardPeer } from '../core/collaboration'\nimport type { DashboardComments } from './comments'\nimport type { DashboardWidget } from '../core/types'\nimport type { DashboardLinkedFilter } from '../data/types'\nimport type { DashboardMessages } from '../locale'\nimport type { DashboardWidgetType } from '../widget/define'\n\nexport type WidgetAction = 'settings' | 'fullscreen' | 'duplicate' | 'remove'\n\n/** An exporter as the export menu lists it. */\nexport interface FrameExporter {\n name: string\n label: string\n icon?: Component\n}\n\n/**\n * What a widget's frame reads from its dashboard. The frame looks its widget\n * up by id in a computed of its own, so a change to one widget draws only\n * that widget's frame again, not every frame on the grid.\n */\nexport interface FrameContext {\n comments: DashboardComments\n widget: (id: string) => DashboardWidget | undefined\n type: (widget: DashboardWidget) => DashboardWidgetType<any> | undefined\n title: (widget: DashboardWidget) => string\n editing: () => boolean\n messages: () => DashboardMessages\n /** Whether a frame mounting now is drawn in full at once. */\n eager: (id: string) => boolean\n /** Whether every widget is drawn in full now, to export or print the dashboard. */\n all: () => boolean\n setProps: (id: string, patch: Record<string, unknown>, options?: { group?: string }) => void\n action: (id: string, action: WidgetAction) => void\n rename: (id: string, title: string) => void\n /** The filters a widget set, shown in its title bar. */\n linked: (id: string) => DashboardLinkedFilter[]\n clearFilter: (key: string) => void\n /** The others at a widget, when editing together. */\n peers: (id: string) => readonly DashboardPeer[]\n /** How a widget gives its content as a workbook; returns what undoes it. */\n setWorkbook: (id: string, get: () => SheetWorkbook | undefined) => () => void\n /** The exporters a widget's menu lists: none unless it gives a workbook. */\n exporters: (id: string) => FrameExporter[]\n /** Writes a widget's content with an exporter, by name, and downloads the file. */\n exportTo: (id: string, exporter: string) => void\n /** Tells whether a widget is drawn in full, not only its title. */\n setDrawn: (id: string, drawn: boolean) => void\n /** Whether a widget's content is being exported now. */\n exporting: (id: string) => boolean\n /** Why the last export from a widget's menu failed. */\n exportError: (id: string) => string | undefined\n clearExportError: (id: string) => void\n}\n\nexport const frameContextKey: InjectionKey<FrameContext> = Symbol('dou-dashboard-frame')\n"],"mappings":";AAyDA,IAAa,kBAA8C,OAAO,qBAAqB"}
1
+ {"version":3,"file":"frame-context.js","names":[],"sources":["../../src/dashboard/frame-context.ts"],"sourcesContent":["import type { SheetWorkbook } from '@dou-ui/sheet/core'\nimport type { Component, InjectionKey } from 'vue'\nimport type { DashboardPeer } from '../core/collaboration'\nimport type { DashboardComments } from './comments'\nimport type { DashboardWidget } from '../core/types'\nimport type { DashboardLinkedFilter } from '../data/types'\nimport type { DashboardMessages } from '../locale'\nimport type { DashboardWidgetType } from '../widget/define'\n\nexport type WidgetAction = 'settings' | 'fullscreen' | 'duplicate' | 'remove'\n\n/** An exporter as the export menu lists it. */\nexport interface FrameExporter {\n name: string\n label: string\n icon?: Component\n}\n\n/**\n * What a widget's frame reads from its dashboard. The frame looks its widget\n * up by id in a computed of its own, so a change to one widget draws only\n * that widget's frame again, not every frame on the grid.\n */\nexport interface FrameContext {\n comments: DashboardComments\n widget: (id: string) => DashboardWidget | undefined\n type: (widget: DashboardWidget) => DashboardWidgetType<any> | undefined\n title: (widget: DashboardWidget) => string\n editing: () => boolean\n messages: () => DashboardMessages\n /** Whether a frame mounting now is drawn in full at once. */\n eager: (id: string) => boolean\n /** Whether every widget is drawn in full now, to export or print the dashboard. */\n all: () => boolean\n setProps: (id: string, patch: Record<string, unknown>, options?: { group?: string }) => void\n action: (id: string, action: WidgetAction) => void\n rename: (id: string, title: string) => void\n /** The filters a widget set, shown in its title bar. */\n linked: (id: string) => DashboardLinkedFilter[]\n clearFilter: (key: string) => void\n /** The others at a widget, when editing together. */\n peers: (id: string) => readonly DashboardPeer[]\n /** How a widget gives its content as a workbook; returns what undoes it. */\n setWorkbook: (id: string, get: () => SheetWorkbook | undefined) => () => void\n /** The exporters a widget's menu lists: none unless it gives a workbook. */\n exporters: (id: string) => FrameExporter[]\n /** Writes a widget's content with an exporter, by name, and downloads the file. */\n exportTo: (id: string, exporter: string) => void\n /** Tells whether a widget is drawn in full, not only its title. */\n setDrawn: (id: string, drawn: boolean) => void\n /** Whether a widget's content is being exported now. */\n exporting: (id: string) => boolean\n /** Why the last export from a widget's menu failed. */\n exportError: (id: string) => string | undefined\n clearExportError: (id: string) => void\n /** A widget's content broke, for `widgetError`. */\n reportError: (id: string, error: unknown) => void\n}\n\nexport const frameContextKey: InjectionKey<FrameContext> = Symbol('dou-dashboard-frame')\n"],"mappings":";AA2DA,IAAa,kBAA8C,OAAO,qBAAqB"}
@@ -1 +1 @@
1
- {"version":3,"file":"widget-body.js","names":["_sfc_main"],"sources":["../../src/dashboard/widget-body.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { AlertCircleIcon } from '@dou-ui/icons'\nimport { DouButton, DouEmpty, DouSpinner } from 'dou-ui'\nimport { inject, onBeforeUnmount, onMounted, ref } from 'vue'\nimport type { SheetWorkbook } from '@dou-ui/sheet/core'\nimport type { DashboardWidget } from '../core/types'\nimport type { DashboardMessages } from '../locale'\nimport { format } from '../locale'\nimport { provideWidgetContext } from '../widget/context'\nimport { observersKey } from './observers'\nimport type { DashboardWidgetType } from '../widget/define'\n\ndefineOptions({ name: 'DouDashboardWidgetBody' })\n\nconst props = defineProps<{\n widget: DashboardWidget\n type?: DashboardWidgetType<any>\n editing: boolean\n fullscreen?: boolean\n messages: DashboardMessages\n setProps: (id: string, patch: Record<string, unknown>, options?: { group?: string }) => void\n /** Where the widget's workbook goes, for its export menu; the full-screen copy has none. */\n setWorkbook?: (id: string, get: () => SheetWorkbook | undefined) => () => void\n}>()\n\nconst body = ref<HTMLElement>()\nconst width = ref(0)\nconst height = ref(0)\nconst observers = inject(observersKey)\nlet stop: (() => void) | undefined\nonMounted(() => {\n // not read here: that would lay the page out once per widget. The first\n // observation comes before the page is painted.\n stop = observers?.size(body.value!, (entry) => {\n width.value = entry.contentRect.width\n height.value = entry.contentRect.height\n })\n})\nonBeforeUnmount(() => stop?.())\nconst { status } = provideWidgetContext({\n widget: () => props.widget,\n editing: () => props.editing,\n fullscreen: () => !!props.fullscreen,\n size: { width, height },\n setProps: props.setProps,\n setWorkbook: props.setWorkbook,\n})\n</script>\n\n<template>\n <div class=\"dou-dashboard__body\" :aria-busy=\"status.state === 'loading' || undefined\">\n <div ref=\"body\" class=\"dou-dashboard__content\">\n <component :is=\"type.component\" v-if=\"type\" />\n <div v-else class=\"dou-dashboard__notice\">\n {{ format(messages.unknownType, { type: widget.type }) }}\n </div>\n </div>\n <div v-if=\"status.state === 'loading'\" class=\"dou-dashboard__status is-loading\" role=\"status\">\n <DouSpinner size=\"sm\" :label=\"messages.loading\" />\n </div>\n <div v-else-if=\"status.state === 'error'\" class=\"dou-dashboard__status is-error\" role=\"alert\">\n <AlertCircleIcon class=\"dou-dashboard__status-icon\" />\n <span>{{ status.message || messages.error }}</span>\n <DouButton v-if=\"status.retry\" size=\"sm\" variant=\"soft\" @click=\"status.retry()\">\n {{ messages.retry }}\n </DouButton>\n </div>\n <div v-else-if=\"status.state === 'empty'\" class=\"dou-dashboard__status is-empty\">\n <DouEmpty size=\"sm\" :title=\"status.message || messages.empty\" />\n </div>\n </div>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"widget-body.js","names":["_sfc_main"],"sources":["../../src/dashboard/widget-body.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { AlertCircleIcon } from '@dou-ui/icons'\nimport { DouButton, DouEmpty, DouSpinner } from 'dou-ui'\nimport { inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport { useErrorBoundary } from './error-boundary'\nimport type { SheetWorkbook } from '@dou-ui/sheet/core'\nimport type { DashboardWidget } from '../core/types'\nimport type { DashboardMessages } from '../locale'\nimport { format } from '../locale'\nimport { provideWidgetContext } from '../widget/context'\nimport { observersKey } from './observers'\nimport type { DashboardWidgetType } from '../widget/define'\n\ndefineOptions({ name: 'DouDashboardWidgetBody' })\n\nconst props = defineProps<{\n widget: DashboardWidget\n type?: DashboardWidgetType<any>\n editing: boolean\n fullscreen?: boolean\n messages: DashboardMessages\n setProps: (id: string, patch: Record<string, unknown>, options?: { group?: string }) => void\n /** Where the widget's workbook goes, for its export menu; the full-screen copy has none. */\n setWorkbook?: (id: string, get: () => SheetWorkbook | undefined) => () => void\n /** Told when the widget's content breaks, for the dashboard's `widgetError`. */\n reportError?: (id: string, error: unknown) => void\n}>()\n\nconst body = ref<HTMLElement>()\nconst width = ref(0)\nconst height = ref(0)\nconst observers = inject(observersKey)\nlet stop: (() => void) | undefined\nonMounted(() => {\n // not read here: that would lay the page out once per widget. The first\n // observation comes before the page is painted.\n stop = observers?.size(body.value!, (entry) => {\n width.value = entry.contentRect.width\n height.value = entry.contentRect.height\n })\n})\nonBeforeUnmount(() => stop?.())\nconst { status, data } = provideWidgetContext({\n widget: () => props.widget,\n editing: () => props.editing,\n fullscreen: () => !!props.fullscreen,\n size: { width, height },\n setProps: props.setProps,\n setWorkbook: props.setWorkbook,\n})\n\n// A widget that throws while it sets up or draws, e.g. for props from JSON\n// it can't use, shows an error in its own card; the other widgets and the\n// page go on. It tries again when its props or data change, or on retry.\nconst failed = useErrorBoundary(\n () => `widget \"${props.widget.id}\" (${props.widget.type})`,\n (error) => props.reportError?.(props.widget.id, error),\n)\nconst retryRender = () => (failed.value = false)\nwatch(() => props.widget.props, retryRender, { deep: true })\nwatch(data, retryRender)\n\n// A retry that fails again brings back a new retry button: focus moves to\n// it, rather than falling to the page, if it was on the old one and\n// nothing took it meanwhile.\nconst root = ref<HTMLElement>()\nlet refocus = false\nfunction retry(again: () => void) {\n refocus = !!root.value?.querySelector('.dou-dashboard__status')?.contains(document.activeElement)\n again()\n nextTick(settle)\n}\n/** After a retry: once it has failed or worked, puts focus back on the new retry button if it failed. */\nfunction settle() {\n if (!refocus || (!failed.value && status.value.state === 'loading')) return\n refocus = false\n if (!failed.value && status.value.state !== 'error') return\n const active = document.activeElement\n if (active && active !== document.body) return\n root.value?.querySelector<HTMLElement>('.dou-dashboard__status button')?.focus()\n}\n// data loads later: settle when it fails or arrives\nwatch(() => status.value.state, settle, { flush: 'post' })\n</script>\n\n<template>\n <div ref=\"root\" class=\"dou-dashboard__body\" :aria-busy=\"status.state === 'loading' || undefined\">\n <div ref=\"body\" class=\"dou-dashboard__content\">\n <template v-if=\"failed\" />\n <component :is=\"type.component\" v-else-if=\"type\" />\n <div v-else class=\"dou-dashboard__notice\">\n {{ format(messages.unknownType, { type: widget.type }) }}\n </div>\n </div>\n <div v-if=\"failed\" class=\"dou-dashboard__status is-error\" role=\"alert\">\n <AlertCircleIcon class=\"dou-dashboard__status-icon\" />\n <span>{{ messages.renderError }}</span>\n <DouButton size=\"sm\" variant=\"soft\" @click=\"retry(retryRender)\">\n {{ messages.retry }}\n </DouButton>\n </div>\n <div\n v-else-if=\"status.state === 'loading'\"\n class=\"dou-dashboard__status is-loading\"\n role=\"status\"\n >\n <DouSpinner size=\"sm\" :label=\"messages.loading\" />\n </div>\n <div v-else-if=\"status.state === 'error'\" class=\"dou-dashboard__status is-error\" role=\"alert\">\n <AlertCircleIcon class=\"dou-dashboard__status-icon\" />\n <span>{{ status.message || messages.error }}</span>\n <DouButton\n v-if=\"status.retry\"\n size=\"sm\"\n variant=\"soft\"\n @click=\"retry(() => status.retry?.())\"\n >\n {{ messages.retry }}\n </DouButton>\n </div>\n <div v-else-if=\"status.state === 'empty'\" class=\"dou-dashboard__status is-empty\">\n <DouEmpty size=\"sm\" :title=\"status.message || messages.empty\" />\n </div>\n </div>\n</template>\n"],"mappings":""}