@dou-ui/dashboard 0.1.0-alpha.3 → 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 (97) 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/core/document.js +10 -1
  10. package/dist/core/document.js.map +1 -1
  11. package/dist/core/history.js +23 -0
  12. package/dist/core/history.js.map +1 -1
  13. package/dist/core/transaction.js +8 -2
  14. package/dist/core/transaction.js.map +1 -1
  15. package/dist/dashboard/comment-button.js +6 -0
  16. package/dist/dashboard/comment-button.js.map +1 -0
  17. package/dist/dashboard/comment-button.vue_vue_type_script_setup_true_lang.js +59 -0
  18. package/dist/dashboard/comment-button.vue_vue_type_script_setup_true_lang.js.map +1 -0
  19. package/dist/dashboard/comment-card.js +6 -0
  20. package/dist/dashboard/comment-card.js.map +1 -0
  21. package/dist/dashboard/comment-card.vue_vue_type_script_setup_true_lang.js +128 -0
  22. package/dist/dashboard/comment-card.vue_vue_type_script_setup_true_lang.js.map +1 -0
  23. package/dist/dashboard/comments.js +144 -0
  24. package/dist/dashboard/comments.js.map +1 -0
  25. package/dist/dashboard/dashboard.js.map +1 -1
  26. package/dist/dashboard/dashboard.vue_vue_type_script_setup_true_lang.js +132 -20
  27. package/dist/dashboard/dashboard.vue_vue_type_script_setup_true_lang.js.map +1 -1
  28. package/dist/dashboard/error-boundary.js +64 -0
  29. package/dist/dashboard/error-boundary.js.map +1 -0
  30. package/dist/dashboard/frame-context.js.map +1 -1
  31. package/dist/dashboard/widget-body.js.map +1 -1
  32. package/dist/dashboard/widget-body.vue_vue_type_script_setup_true_lang.js +53 -12
  33. package/dist/dashboard/widget-body.vue_vue_type_script_setup_true_lang.js.map +1 -1
  34. package/dist/dashboard/widget-frame.js.map +1 -1
  35. package/dist/dashboard/widget-frame.vue_vue_type_script_setup_true_lang.js +14 -2
  36. package/dist/dashboard/widget-frame.vue_vue_type_script_setup_true_lang.js.map +1 -1
  37. package/dist/dashboard/widget-settings.js.map +1 -1
  38. package/dist/dashboard/widget-settings.vue_vue_type_script_setup_true_lang.js +31 -5
  39. package/dist/dashboard/widget-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/locale.js +12 -0
  42. package/dist/locale.js.map +1 -1
  43. package/dist/pivot/pivot-settings.js.map +1 -1
  44. package/dist/pivot/pivot-settings.vue_vue_type_script_setup_true_lang.js +6 -4
  45. package/dist/pivot/pivot-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  46. package/dist/pivot/pivot.js.map +1 -1
  47. package/dist/pivot/pivot.vue_vue_type_script_setup_true_lang.js +4 -2
  48. package/dist/pivot/pivot.vue_vue_type_script_setup_true_lang.js.map +1 -1
  49. package/dist/pivot/read-props.js +44 -0
  50. package/dist/pivot/read-props.js.map +1 -0
  51. package/dist/style.css +1 -1
  52. package/dist/style.js +2 -3
  53. package/dist/types/chart/read-props.d.ts +3 -0
  54. package/dist/types/core/document.d.ts +3 -2
  55. package/dist/types/core/history.d.ts +7 -1
  56. package/dist/types/core/transaction.d.ts +10 -1
  57. package/dist/types/core/types.d.ts +5 -0
  58. package/dist/types/dashboard/comment-button.vue.d.ts +15 -0
  59. package/dist/types/dashboard/comment-card.vue.d.ts +13 -0
  60. package/dist/types/dashboard/comments.d.ts +44 -0
  61. package/dist/types/dashboard/dashboard.vue.d.ts +12 -0
  62. package/dist/types/dashboard/error-boundary.d.ts +12 -0
  63. package/dist/types/dashboard/frame-context.d.ts +4 -0
  64. package/dist/types/dashboard/types.d.ts +47 -0
  65. package/dist/types/dashboard/widget-body.vue.d.ts +2 -0
  66. package/dist/types/dashboard/widget-settings.vue.d.ts +4 -0
  67. package/dist/types/index.d.ts +22 -1
  68. package/dist/types/locale.d.ts +8 -0
  69. package/dist/types/pivot/read-props.d.ts +3 -0
  70. package/dist/types/widgets/index.d.ts +1 -1
  71. package/dist/types/widgets/read-props.d.ts +4 -0
  72. package/dist/types/widgets/shared.d.ts +20 -4
  73. package/dist/widgets/index.js +2 -2
  74. package/dist/widgets/index.js.map +1 -1
  75. package/dist/widgets/read-props.js +52 -0
  76. package/dist/widgets/read-props.js.map +1 -0
  77. package/dist/widgets/shared.js +27 -5
  78. package/dist/widgets/shared.js.map +1 -1
  79. package/dist/widgets/stat-settings.js.map +1 -1
  80. package/dist/widgets/stat-settings.vue_vue_type_script_setup_true_lang.js +12 -10
  81. package/dist/widgets/stat-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  82. package/dist/widgets/stat.js.map +1 -1
  83. package/dist/widgets/stat.vue_vue_type_script_setup_true_lang.js +10 -13
  84. package/dist/widgets/stat.vue_vue_type_script_setup_true_lang.js.map +1 -1
  85. package/dist/widgets/table-settings.js.map +1 -1
  86. package/dist/widgets/table-settings.vue_vue_type_script_setup_true_lang.js +6 -4
  87. package/dist/widgets/table-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  88. package/dist/widgets/table.js.map +1 -1
  89. package/dist/widgets/table.vue_vue_type_script_setup_true_lang.js +3 -1
  90. package/dist/widgets/table.vue_vue_type_script_setup_true_lang.js.map +1 -1
  91. package/dist/widgets/text-settings.js.map +1 -1
  92. package/dist/widgets/text-settings.vue_vue_type_script_setup_true_lang.js +5 -3
  93. package/dist/widgets/text-settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
  94. package/dist/widgets/text.js.map +1 -1
  95. package/dist/widgets/text.vue_vue_type_script_setup_true_lang.js +5 -3
  96. package/dist/widgets/text.vue_vue_type_script_setup_true_lang.js.map +1 -1
  97. package/package.json +8 -8
@@ -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 { 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 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":";AAuDA,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":""}
@@ -1,27 +1,33 @@
1
1
  import { format } from "../locale.js";
2
2
  import { observersKey } from "./observers.js";
3
+ import { useErrorBoundary } from "./error-boundary.js";
3
4
  import { provideWidgetContext } from "../widget/context.js";
4
5
  import { DouButton, DouEmpty, DouSpinner } from "dou-ui";
5
- import { createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, onBeforeUnmount, onMounted, openBlock, ref, resolveDynamicComponent, toDisplayString, unref, withCtx } from "vue";
6
+ import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, nextTick, onBeforeUnmount, onMounted, openBlock, ref, resolveDynamicComponent, toDisplayString, unref, watch, withCtx } from "vue";
6
7
  import { AlertCircleIcon } from "@dou-ui/icons";
7
8
  // src/dashboard/widget-body.vue?vue&type=script&setup=true&lang.ts
8
9
  var _hoisted_1 = ["aria-busy"];
9
10
  var _hoisted_2 = {
10
- key: 1,
11
+ key: 2,
11
12
  class: "dou-dashboard__notice"
12
13
  };
13
14
  var _hoisted_3 = {
14
15
  key: 0,
15
- class: "dou-dashboard__status is-loading",
16
- role: "status"
16
+ class: "dou-dashboard__status is-error",
17
+ role: "alert"
17
18
  };
18
19
  var _hoisted_4 = {
19
20
  key: 1,
20
- class: "dou-dashboard__status is-error",
21
- role: "alert"
21
+ class: "dou-dashboard__status is-loading",
22
+ role: "status"
22
23
  };
23
24
  var _hoisted_5 = {
24
25
  key: 2,
26
+ class: "dou-dashboard__status is-error",
27
+ role: "alert"
28
+ };
29
+ var _hoisted_6 = {
30
+ key: 3,
25
31
  class: "dou-dashboard__status is-empty"
26
32
  };
27
33
  var widget_body_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
@@ -34,7 +40,8 @@ var widget_body_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
34
40
  fullscreen: { type: Boolean },
35
41
  messages: {},
36
42
  setProps: { type: Function },
37
- setWorkbook: { type: Function }
43
+ setWorkbook: { type: Function },
44
+ reportError: { type: Function }
38
45
  },
39
46
  setup(__props) {
40
47
  const props = __props;
@@ -50,7 +57,7 @@ var widget_body_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
50
57
  });
51
58
  });
52
59
  onBeforeUnmount(() => stop?.());
53
- const { status } = provideWidgetContext({
60
+ const { status, data } = provideWidgetContext({
54
61
  widget: () => props.widget,
55
62
  editing: () => props.editing,
56
63
  fullscreen: () => !!props.fullscreen,
@@ -61,30 +68,64 @@ var widget_body_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
61
68
  setProps: props.setProps,
62
69
  setWorkbook: props.setWorkbook
63
70
  });
71
+ const failed = useErrorBoundary(() => `widget "${props.widget.id}" (${props.widget.type})`, (error) => props.reportError?.(props.widget.id, error));
72
+ const retryRender = () => failed.value = false;
73
+ watch(() => props.widget.props, retryRender, { deep: true });
74
+ watch(data, retryRender);
75
+ const root = ref();
76
+ let refocus = false;
77
+ function retry(again) {
78
+ refocus = !!root.value?.querySelector(".dou-dashboard__status")?.contains(document.activeElement);
79
+ again();
80
+ nextTick(settle);
81
+ }
82
+ /** After a retry: once it has failed or worked, puts focus back on the new retry button if it failed. */
83
+ function settle() {
84
+ if (!refocus || !failed.value && status.value.state === "loading") return;
85
+ refocus = false;
86
+ if (!failed.value && status.value.state !== "error") return;
87
+ const active = document.activeElement;
88
+ if (active && active !== document.body) return;
89
+ root.value?.querySelector(".dou-dashboard__status button")?.focus();
90
+ }
91
+ watch(() => status.value.state, settle, { flush: "post" });
64
92
  return (_ctx, _cache) => {
65
93
  return openBlock(), createElementBlock("div", {
94
+ ref_key: "root",
95
+ ref: root,
66
96
  class: "dou-dashboard__body",
67
97
  "aria-busy": unref(status).state === "loading" || void 0
68
98
  }, [createElementVNode("div", {
69
99
  ref_key: "body",
70
100
  ref: body,
71
101
  class: "dou-dashboard__content"
72
- }, [__props.type ? (openBlock(), createBlock(resolveDynamicComponent(__props.type.component), { key: 0 })) : (openBlock(), createElementBlock("div", _hoisted_2, toDisplayString(unref(format)(__props.messages.unknownType, { type: __props.widget.type })), 1))], 512), unref(status).state === "loading" ? (openBlock(), createElementBlock("div", _hoisted_3, [createVNode(unref(DouSpinner), {
102
+ }, [unref(failed) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [], 64)) : __props.type ? (openBlock(), createBlock(resolveDynamicComponent(__props.type.component), { key: 1 })) : (openBlock(), createElementBlock("div", _hoisted_2, toDisplayString(unref(format)(__props.messages.unknownType, { type: __props.widget.type })), 1))], 512), unref(failed) ? (openBlock(), createElementBlock("div", _hoisted_3, [
103
+ createVNode(unref(AlertCircleIcon), { class: "dou-dashboard__status-icon" }),
104
+ createElementVNode("span", null, toDisplayString(__props.messages.renderError), 1),
105
+ createVNode(unref(DouButton), {
106
+ size: "sm",
107
+ variant: "soft",
108
+ onClick: _cache[0] || (_cache[0] = ($event) => retry(retryRender))
109
+ }, {
110
+ default: withCtx(() => [createTextVNode(toDisplayString(__props.messages.retry), 1)]),
111
+ _: 1
112
+ })
113
+ ])) : unref(status).state === "loading" ? (openBlock(), createElementBlock("div", _hoisted_4, [createVNode(unref(DouSpinner), {
73
114
  size: "sm",
74
115
  label: __props.messages.loading
75
- }, null, 8, ["label"])])) : unref(status).state === "error" ? (openBlock(), createElementBlock("div", _hoisted_4, [
116
+ }, null, 8, ["label"])])) : unref(status).state === "error" ? (openBlock(), createElementBlock("div", _hoisted_5, [
76
117
  createVNode(unref(AlertCircleIcon), { class: "dou-dashboard__status-icon" }),
77
118
  createElementVNode("span", null, toDisplayString(unref(status).message || __props.messages.error), 1),
78
119
  unref(status).retry ? (openBlock(), createBlock(unref(DouButton), {
79
120
  key: 0,
80
121
  size: "sm",
81
122
  variant: "soft",
82
- onClick: _cache[0] || (_cache[0] = ($event) => unref(status).retry())
123
+ onClick: _cache[1] || (_cache[1] = ($event) => retry(() => unref(status).retry?.()))
83
124
  }, {
84
125
  default: withCtx(() => [createTextVNode(toDisplayString(__props.messages.retry), 1)]),
85
126
  _: 1
86
127
  })) : createCommentVNode("", true)
87
- ])) : unref(status).state === "empty" ? (openBlock(), createElementBlock("div", _hoisted_5, [createVNode(unref(DouEmpty), {
128
+ ])) : unref(status).state === "empty" ? (openBlock(), createElementBlock("div", _hoisted_6, [createVNode(unref(DouEmpty), {
88
129
  size: "sm",
89
130
  title: unref(status).message || __props.messages.empty
90
131
  }, null, 8, ["title"])])) : createCommentVNode("", true)], 8, _hoisted_1);
@@ -1 +1 @@
1
- {"version":3,"file":"widget-body.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_unref","_createElementVNode","_createBlock","_resolveDynamicComponent","_toDisplayString","_createVNode","_withCtx","_createTextVNode","_createCommentVNode"],"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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcA,MAAM,QAAQ;EAWd,MAAM,OAAO,IAAiB;EAC9B,MAAM,QAAQ,IAAI,CAAC;EACnB,MAAM,SAAS,IAAI,CAAC;EACpB,MAAM,YAAY,OAAO,YAAY;EACrC,IAAI;EACJ,gBAAgB;GAGd,OAAO,WAAW,KAAK,KAAK,QAAS,UAAU;IAC7C,MAAM,QAAQ,MAAM,YAAY;IAChC,OAAO,QAAQ,MAAM,YAAY;GACnC,CAAC;EACH,CAAC;EACD,sBAAsB,OAAO,CAAC;EAC9B,MAAM,EAAE,WAAW,qBAAqB;GACtC,cAAc,MAAM;GACpB,eAAe,MAAM;GACrB,kBAAkB,CAAC,CAAC,MAAM;GAC1B,MAAM;IAAE;IAAO;GAAO;GACtB,UAAU,MAAM;GAChB,aAAa,MAAM;EACrB,CAAC;;GAIC,OAAAA,UAAA,GAAAC,mBAoBM,OAAA;IApBD,OAAM;IAAuB,aAAWC,MAAA,MAAA,CAAM,CAAC,UAAK,aAAkB,KAAA;G,IACzEC,mBAKM,OAAA;IALG,SAAA;IAAJ,KAAI;IAAO,OAAM;G,IACkB,QAAA,QAAtCH,UAAA,GAAAI,YAA8CC,wBAA9B,QAAA,KAAK,SAAS,GAAA,EAAA,KAAA,EAAA,CAAA,MAC9BL,UAAA,GAAAC,mBAEM,OAFN,YAEMK,gBADDJ,MAAA,MAAA,CAAM,CAAC,QAAA,SAAS,aAAW,EAAA,MAAU,QAAA,OAAO,KAAI,CAAA,CAAA,GAAA,CAAA,E,SAG5CA,MAAA,MAAA,CAAM,CAAC,UAAK,aAAvBF,UAAA,GAAAC,mBAEM,OAFN,YAEM,CADJM,YAAkDL,MAAA,UAAA,GAAA;IAAtC,MAAK;IAAM,OAAO,QAAA,SAAS;G,4BAEzBA,MAAA,MAAA,CAAM,CAAC,UAAK,WAA5BF,UAAA,GAAAC,mBAMM,OANN,YAMM;IALJM,YAAsDL,MAAA,eAAA,GAAA,EAArC,OAAM,6BAA4B,CAAA;IACnDC,mBAAmD,QAAA,MAAAG,gBAA1CJ,MAAA,MAAA,CAAM,CAAC,WAAW,QAAA,SAAS,KAAK,GAAA,CAAA;IACxBA,MAAA,MAAA,CAAM,CAAC,SAAxBF,UAAA,GAAAI,YAEYF,MAAA,SAAA,GAAA;;KAFmB,MAAK;KAAK,SAAQ;KAAQ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAEA,MAAA,MAAA,CAAM,CAAC,MAAK;;KAC1E,SAAAM,cAAoB,CAAjBC,gBAAAH,gBAAA,QAAA,SAAS,KAAK,GAAA,CAAA,C;;;G,MAGLJ,MAAA,MAAA,CAAM,CAAC,UAAK,WAA5BF,UAAA,GAAAC,mBAEM,OAFN,YAEM,CADJM,YAAgEL,MAAA,QAAA,GAAA;IAAtD,MAAK;IAAM,OAAOA,MAAA,MAAA,CAAM,CAAC,WAAW,QAAA,SAAS"}
1
+ {"version":3,"file":"widget-body.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_unref","_createElementVNode","_Fragment","_createBlock","_resolveDynamicComponent","_toDisplayString","_createVNode","_withCtx","_createTextVNode","_createCommentVNode"],"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":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"widget-frame.js","names":["_sfc_main"],"sources":["../../src/dashboard/widget-frame.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DownloadIcon, MaximizeIcon, MoreHorizontalIcon } from '@dou-ui/icons'\nimport {\n DouAlert,\n DouButton,\n DouDropdown,\n DouDropdownItem,\n DouDropdownSub,\n DouEditable,\n DouTag,\n DouTooltip,\n} from 'dou-ui'\nimport type { DropdownValue } from 'dou-ui'\nimport { computed, inject, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport GridLayoutHandle from '../grid/grid-handle.vue'\nimport { format } from '../locale'\nimport { frameContextKey } from './frame-context'\nimport type { WidgetAction } from './frame-context'\nimport { observersKey } from './observers'\nimport WidgetBody from './widget-body.vue'\n\ndefineOptions({ name: 'DouDashboardWidget' })\n\nconst props = defineProps<{ id: string }>()\n\nconst frame = inject(frameContextKey)!\nconst widget = computed(() => frame.widget(props.id))\nconst type = computed(() => widget.value && frame.type(widget.value))\nconst title = computed(() => (widget.value ? frame.title(widget.value) : ''))\nconst editing = computed(() => frame.editing())\nconst messages = computed(() => frame.messages())\n/** The filters this widget set, each with a button to clear it. */\nconst linked = computed(() => frame.linked(props.id))\n/** All of them in one tag (a pivot table's click sets one per field), cleared together. */\nconst linkLabel = computed(() => linked.value.map((link) => link.label).join(' / '))\nfunction clearLinks() {\n for (const link of linked.value) frame.clearFilter(link.key)\n}\n\n/** Others at this widget, when editing together: an outline in the first one's color and their names. */\nconst peers = computed(() => frame.peers(props.id))\nconst peerText = (peer: { name: string; action: string }) => {\n const m = messages.value\n const text =\n peer.action === 'move'\n ? m.peerMoving\n : peer.action === 'resize'\n ? m.peerResizing\n : peer.action === 'settings'\n ? m.peerSettings\n : '{name}'\n return format(text, { name: peer.name })\n}\n\nconst renaming = ref(false)\n\n// Widgets far out of view show only their title until they come near: with\n// hundreds of widgets, their contents and controls are most of the mounting.\nconst root = ref<HTMLElement>()\nconst active = ref(frame.eager(props.id))\nconst observers = inject(observersKey)\nlet stop: (() => void) | undefined\nonMounted(() => {\n if (active.value || !observers) {\n active.value = true\n return\n }\n stop = observers.near(root.value!, () => {\n active.value = true\n stop?.()\n })\n})\nonBeforeUnmount(() => stop?.())\n// the dashboard draws everything for an export only when this widget isn't drawn yet\nwatch(active, (drawn) => frame.setDrawn(props.id, drawn), { immediate: true })\nonBeforeUnmount(() => frame.setDrawn(props.id, false))\n// drawn in full to export or print the dashboard, and kept that way\nwatch(\n () => frame.all(),\n (all) => {\n if (!all || active.value) return\n active.value = true\n stop?.()\n },\n { immediate: true },\n)\nconst canFullscreen = computed(() => type.value?.fullscreen !== false)\n\nconst emitAction = (action: WidgetAction) => frame.action(props.id, action)\n\n/** The file types this widget's content can be exported as; none unless it gives a workbook. */\nconst exporters = computed(() => frame.exporters(props.id))\nconst exportError = computed(() => frame.exportError(props.id))\nconst exporting = computed(() => frame.exporting(props.id))\nconst EXPORT = 'export:'\nconst exportOpen = ref(false)\n\nfunction onSelect(value: DropdownValue | undefined) {\n if (typeof value !== 'string') return\n if (value.startsWith(EXPORT)) frame.exportTo(props.id, value.slice(EXPORT.length))\n else emitAction(value as WidgetAction)\n}\n</script>\n\n<template>\n <!-- outside the frame, which cuts off what overflows it -->\n <div v-if=\"widget && peers.length\" class=\"dou-dashboard__peers\" aria-hidden=\"true\">\n <span\n v-for=\"peer in peers\"\n :key=\"`${peer.client}:${peer.action}`\"\n :class=\"['dou-dashboard__peer', `is-${peer.action}`]\"\n :style=\"{ '--dou-dashboard-peer-color': peer.color }\"\n >\n {{ peerText(peer) }}\n </span>\n </div>\n <div\n v-if=\"widget\"\n ref=\"root\"\n :class=\"['dou-dashboard__widget', peers.length && 'has-peers']\"\n :style=\"peers.length ? { '--dou-dashboard-peer-color': peers[0]!.color } : undefined\"\n >\n <template v-if=\"!active\">\n <div class=\"dou-dashboard__header\">\n <div class=\"dou-dashboard__title\">{{ title }}</div>\n </div>\n <div class=\"dou-dashboard__body\" />\n </template>\n <component\n v-else\n :is=\"editing ? GridLayoutHandle : 'div'\"\n :as=\"editing ? 'header' : undefined\"\n class=\"dou-dashboard__header\"\n >\n <DouEditable\n v-if=\"editing\"\n v-model:editing=\"renaming\"\n class=\"dou-dashboard__title\"\n :model-value=\"title\"\n trigger=\"dblclick\"\n select-on-edit\n :aria-label=\"messages.titleField\"\n :data-dou-nodrag=\"renaming ? '' : undefined\"\n @submit=\"(value) => frame.rename(id, value)\"\n />\n <div v-else class=\"dou-dashboard__title\">{{ title }}</div>\n <div v-if=\"linked.length\" class=\"dou-dashboard__links\" data-dou-nodrag>\n <DouTag\n size=\"sm\"\n closable\n :close-label=\"format(messages.clearFilter, { value: linkLabel })\"\n @close=\"clearLinks\"\n >\n {{ format(messages.filteredBy, { value: linkLabel }) }}\n </DouTag>\n </div>\n <div class=\"dou-dashboard__actions\" data-dou-nodrag>\n <DouDropdown v-if=\"editing\" placement=\"bottom-end\" @select=\"onSelect\">\n <DouButton size=\"sm\" variant=\"ghost\" :aria-label=\"messages.widgetMenu\">\n <MoreHorizontalIcon />\n </DouButton>\n <template #menu>\n <DouDropdownItem value=\"settings\">{{ messages.settings }}</DouDropdownItem>\n <DouDropdownItem v-if=\"canFullscreen\" value=\"fullscreen\">\n {{ messages.fullscreen }}\n </DouDropdownItem>\n <DouDropdownSub v-if=\"exporters.length\" :title=\"messages.export\">\n <DouDropdownItem\n v-for=\"exporter in exporters\"\n :key=\"exporter.name\"\n :value=\"EXPORT + exporter.name\"\n :disabled=\"exporting\"\n >\n <template v-if=\"exporter.icon\" #prefix><component :is=\"exporter.icon\" /></template>\n {{ exporter.label }}\n </DouDropdownItem>\n </DouDropdownSub>\n <DouDropdownItem value=\"duplicate\" shortcut=\"mod+d\">\n {{ messages.duplicate }}\n </DouDropdownItem>\n <DouDropdownItem value=\"remove\" tone=\"danger\" shortcut=\"delete\" divided>\n {{ messages.remove }}\n </DouDropdownItem>\n </template>\n </DouDropdown>\n <DouTooltip v-if=\"!editing && exporters.length\" :content=\"messages.export\">\n <DouDropdown v-model:open=\"exportOpen\" placement=\"bottom-end\" @select=\"onSelect\">\n <DouButton\n size=\"sm\"\n variant=\"ghost\"\n :aria-label=\"messages.export\"\n :loading=\"exporting\"\n :disabled=\"exporting\"\n >\n <DownloadIcon />\n </DouButton>\n <template #menu>\n <DouDropdownItem\n v-for=\"exporter in exporters\"\n :key=\"exporter.name\"\n :value=\"EXPORT + exporter.name\"\n >\n <template v-if=\"exporter.icon\" #prefix><component :is=\"exporter.icon\" /></template>\n {{ exporter.label }}\n </DouDropdownItem>\n </template>\n </DouDropdown>\n </DouTooltip>\n <DouTooltip v-if=\"!editing && canFullscreen\" :content=\"messages.fullscreen\">\n <DouButton\n size=\"sm\"\n variant=\"ghost\"\n :aria-label=\"messages.fullscreen\"\n @click=\"emitAction('fullscreen')\"\n >\n <MaximizeIcon />\n </DouButton>\n </DouTooltip>\n </div>\n </component>\n <div v-if=\"exportError\" class=\"dou-dashboard__export-error\" data-dou-nodrag>\n <DouAlert tone=\"danger\" closable @close=\"frame.clearExportError(id)\">\n {{ exportError }}\n </DouAlert>\n </div>\n <WidgetBody\n v-if=\"active\"\n :widget=\"widget\"\n :type=\"type\"\n :editing=\"editing\"\n :messages=\"messages\"\n :set-props=\"frame.setProps\"\n :set-workbook=\"frame.setWorkbook\"\n />\n </div>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"widget-frame.js","names":["_sfc_main"],"sources":["../../src/dashboard/widget-frame.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DownloadIcon, MaximizeIcon, MoreHorizontalIcon } from '@dou-ui/icons'\nimport {\n DouAlert,\n DouButton,\n DouDropdown,\n DouDropdownItem,\n DouDropdownSub,\n DouEditable,\n DouTag,\n DouTooltip,\n} from 'dou-ui'\nimport type { DropdownValue } from 'dou-ui'\nimport { computed, inject, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport GridLayoutHandle from '../grid/grid-handle.vue'\nimport { format } from '../locale'\nimport { frameContextKey } from './frame-context'\nimport type { WidgetAction } from './frame-context'\nimport { observersKey } from './observers'\nimport CommentButton from './comment-button.vue'\nimport WidgetBody from './widget-body.vue'\n\ndefineOptions({ name: 'DouDashboardWidget' })\n\nconst props = defineProps<{ id: string }>()\n\nconst frame = inject(frameContextKey)!\nconst widget = computed(() => frame.widget(props.id))\nconst type = computed(() => widget.value && frame.type(widget.value))\nconst title = computed(() => (widget.value ? frame.title(widget.value) : ''))\nconst editing = computed(() => frame.editing())\nconst messages = computed(() => frame.messages())\n/** The filters this widget set, each with a button to clear it. */\nconst linked = computed(() => frame.linked(props.id))\n/** All of them in one tag (a pivot table's click sets one per field), cleared together. */\nconst linkLabel = computed(() => linked.value.map((link) => link.label).join(' / '))\nfunction clearLinks() {\n for (const link of linked.value) frame.clearFilter(link.key)\n}\n\n/** Others at this widget, when editing together: an outline in the first one's color and their names. */\nconst peers = computed(() => frame.peers(props.id))\nconst peerText = (peer: { name: string; action: string }) => {\n const m = messages.value\n const text =\n peer.action === 'move'\n ? m.peerMoving\n : peer.action === 'resize'\n ? m.peerResizing\n : peer.action === 'settings'\n ? m.peerSettings\n : '{name}'\n return format(text, { name: peer.name })\n}\n\nconst renaming = ref(false)\n\n// Widgets far out of view show only their title until they come near: with\n// hundreds of widgets, their contents and controls are most of the mounting.\nconst root = ref<HTMLElement>()\nconst active = ref(frame.eager(props.id))\nconst observers = inject(observersKey)\nlet stop: (() => void) | undefined\nonMounted(() => {\n if (active.value || !observers) {\n active.value = true\n return\n }\n stop = observers.near(root.value!, () => {\n active.value = true\n stop?.()\n })\n})\nonBeforeUnmount(() => stop?.())\n// the dashboard draws everything for an export only when this widget isn't drawn yet\nwatch(active, (drawn) => frame.setDrawn(props.id, drawn), { immediate: true })\nonBeforeUnmount(() => frame.setDrawn(props.id, false))\n// drawn in full to export or print the dashboard, and kept that way\nwatch(\n () => frame.all(),\n (all) => {\n if (!all || active.value) return\n active.value = true\n stop?.()\n },\n { immediate: true },\n)\nconst canFullscreen = computed(() => type.value?.fullscreen !== false)\n\nconst emitAction = (action: WidgetAction) => frame.action(props.id, action)\n\n/** The file types this widget's content can be exported as; none unless it gives a workbook. */\nconst exporters = computed(() => frame.exporters(props.id))\nconst exportError = computed(() => frame.exportError(props.id))\nconst exporting = computed(() => frame.exporting(props.id))\nconst EXPORT = 'export:'\nconst exportOpen = ref(false)\n\nfunction onSelect(value: DropdownValue | undefined) {\n if (typeof value !== 'string') return\n if (value.startsWith(EXPORT)) frame.exportTo(props.id, value.slice(EXPORT.length))\n else emitAction(value as WidgetAction)\n}\n</script>\n\n<template>\n <!-- outside the frame, which cuts off what overflows it -->\n <div v-if=\"widget && peers.length\" class=\"dou-dashboard__peers\" aria-hidden=\"true\">\n <span\n v-for=\"peer in peers\"\n :key=\"`${peer.client}:${peer.action}`\"\n :class=\"['dou-dashboard__peer', `is-${peer.action}`]\"\n :style=\"{ '--dou-dashboard-peer-color': peer.color }\"\n >\n {{ peerText(peer) }}\n </span>\n </div>\n <div\n v-if=\"widget\"\n ref=\"root\"\n :class=\"['dou-dashboard__widget', peers.length && 'has-peers']\"\n :style=\"peers.length ? { '--dou-dashboard-peer-color': peers[0]!.color } : undefined\"\n >\n <template v-if=\"!active\">\n <div class=\"dou-dashboard__header\">\n <div class=\"dou-dashboard__title\">{{ title }}</div>\n </div>\n <div class=\"dou-dashboard__body\" />\n </template>\n <component\n v-else\n :is=\"editing ? GridLayoutHandle : 'div'\"\n :as=\"editing ? 'header' : undefined\"\n class=\"dou-dashboard__header\"\n >\n <DouEditable\n v-if=\"editing\"\n v-model:editing=\"renaming\"\n class=\"dou-dashboard__title\"\n :model-value=\"title\"\n trigger=\"dblclick\"\n select-on-edit\n :aria-label=\"messages.titleField\"\n :data-dou-nodrag=\"renaming ? '' : undefined\"\n @submit=\"(value) => frame.rename(id, value)\"\n />\n <div v-else class=\"dou-dashboard__title\">{{ title }}</div>\n <div v-if=\"linked.length\" class=\"dou-dashboard__links\" data-dou-nodrag>\n <DouTag\n size=\"sm\"\n closable\n :close-label=\"format(messages.clearFilter, { value: linkLabel })\"\n @close=\"clearLinks\"\n >\n {{ format(messages.filteredBy, { value: linkLabel }) }}\n </DouTag>\n </div>\n <div class=\"dou-dashboard__actions\" data-dou-nodrag>\n <CommentButton :id=\"id\" :comments=\"frame.comments\" :messages=\"messages\" />\n <DouDropdown v-if=\"editing\" placement=\"bottom-end\" @select=\"onSelect\">\n <DouButton size=\"sm\" variant=\"ghost\" :aria-label=\"messages.widgetMenu\">\n <MoreHorizontalIcon />\n </DouButton>\n <template #menu>\n <DouDropdownItem value=\"settings\">{{ messages.settings }}</DouDropdownItem>\n <DouDropdownItem v-if=\"canFullscreen\" value=\"fullscreen\">\n {{ messages.fullscreen }}\n </DouDropdownItem>\n <DouDropdownSub v-if=\"exporters.length\" :title=\"messages.export\">\n <DouDropdownItem\n v-for=\"exporter in exporters\"\n :key=\"exporter.name\"\n :value=\"EXPORT + exporter.name\"\n :disabled=\"exporting\"\n >\n <template v-if=\"exporter.icon\" #prefix><component :is=\"exporter.icon\" /></template>\n {{ exporter.label }}\n </DouDropdownItem>\n </DouDropdownSub>\n <DouDropdownItem value=\"duplicate\" shortcut=\"mod+d\">\n {{ messages.duplicate }}\n </DouDropdownItem>\n <DouDropdownItem value=\"remove\" tone=\"danger\" shortcut=\"delete\" divided>\n {{ messages.remove }}\n </DouDropdownItem>\n </template>\n </DouDropdown>\n <DouTooltip v-if=\"!editing && exporters.length\" :content=\"messages.export\">\n <DouDropdown v-model:open=\"exportOpen\" placement=\"bottom-end\" @select=\"onSelect\">\n <DouButton\n size=\"sm\"\n variant=\"ghost\"\n :aria-label=\"messages.export\"\n :loading=\"exporting\"\n :disabled=\"exporting\"\n >\n <DownloadIcon />\n </DouButton>\n <template #menu>\n <DouDropdownItem\n v-for=\"exporter in exporters\"\n :key=\"exporter.name\"\n :value=\"EXPORT + exporter.name\"\n >\n <template v-if=\"exporter.icon\" #prefix><component :is=\"exporter.icon\" /></template>\n {{ exporter.label }}\n </DouDropdownItem>\n </template>\n </DouDropdown>\n </DouTooltip>\n <DouTooltip v-if=\"!editing && canFullscreen\" :content=\"messages.fullscreen\">\n <DouButton\n size=\"sm\"\n variant=\"ghost\"\n :aria-label=\"messages.fullscreen\"\n @click=\"emitAction('fullscreen')\"\n >\n <MaximizeIcon />\n </DouButton>\n </DouTooltip>\n </div>\n </component>\n <div v-if=\"exportError\" class=\"dou-dashboard__export-error\" data-dou-nodrag>\n <DouAlert tone=\"danger\" closable @close=\"frame.clearExportError(id)\">\n {{ exportError }}\n </DouAlert>\n </div>\n <WidgetBody\n v-if=\"active\"\n :widget=\"widget\"\n :type=\"type\"\n :editing=\"editing\"\n :messages=\"messages\"\n :set-props=\"frame.setProps\"\n :set-workbook=\"frame.setWorkbook\"\n :report-error=\"frame.reportError\"\n />\n </div>\n</template>\n"],"mappings":""}
@@ -3,6 +3,7 @@ import grid_handle_default from "../grid/grid-handle.js";
3
3
  import { observersKey } from "./observers.js";
4
4
  import widget_body_default from "./widget-body.js";
5
5
  import { frameContextKey } from "./frame-context.js";
6
+ import comment_button_default from "./comment-button.js";
6
7
  import { DouAlert, DouButton, DouDropdown, DouDropdownItem, DouDropdownSub, DouEditable, DouTag, DouTooltip } from "dou-ui";
7
8
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, inject, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, resolveDynamicComponent, toDisplayString, unref, watch, withCtx } from "vue";
8
9
  import { DownloadIcon, MaximizeIcon, MoreHorizontalIcon } from "@dou-ui/icons";
@@ -141,6 +142,15 @@ var widget_frame_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
141
142
  _: 1
142
143
  }, 8, ["close-label"])])) : createCommentVNode("", true),
143
144
  createElementVNode("div", _hoisted_6, [
145
+ createVNode(comment_button_default, {
146
+ id: __props.id,
147
+ comments: unref(frame).comments,
148
+ messages: messages.value
149
+ }, null, 8, [
150
+ "id",
151
+ "comments",
152
+ "messages"
153
+ ]),
144
154
  editing.value ? (openBlock(), createBlock(unref(DouDropdown), {
145
155
  key: 0,
146
156
  placement: "bottom-end",
@@ -280,14 +290,16 @@ var widget_frame_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
280
290
  editing: editing.value,
281
291
  messages: messages.value,
282
292
  "set-props": unref(frame).setProps,
283
- "set-workbook": unref(frame).setWorkbook
293
+ "set-workbook": unref(frame).setWorkbook,
294
+ "report-error": unref(frame).reportError
284
295
  }, null, 8, [
285
296
  "widget",
286
297
  "type",
287
298
  "editing",
288
299
  "messages",
289
300
  "set-props",
290
- "set-workbook"
301
+ "set-workbook",
302
+ "report-error"
291
303
  ])) : createCommentVNode("", true)
292
304
  ], 6)) : createCommentVNode("", true)], 64);
293
305
  };
@@ -1 +1 @@
1
- {"version":3,"file":"widget-frame.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_Fragment","_renderList","_normalizeClass","_normalizeStyle","_toDisplayString","_createCommentVNode","_createElementVNode","_createBlock","_resolveDynamicComponent","GridLayoutHandle","_withCtx","_unref","_createVNode","_createTextVNode","_createSlots","WidgetBody"],"sources":["../../src/dashboard/widget-frame.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DownloadIcon, MaximizeIcon, MoreHorizontalIcon } from '@dou-ui/icons'\nimport {\n DouAlert,\n DouButton,\n DouDropdown,\n DouDropdownItem,\n DouDropdownSub,\n DouEditable,\n DouTag,\n DouTooltip,\n} from 'dou-ui'\nimport type { DropdownValue } from 'dou-ui'\nimport { computed, inject, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport GridLayoutHandle from '../grid/grid-handle.vue'\nimport { format } from '../locale'\nimport { frameContextKey } from './frame-context'\nimport type { WidgetAction } from './frame-context'\nimport { observersKey } from './observers'\nimport WidgetBody from './widget-body.vue'\n\ndefineOptions({ name: 'DouDashboardWidget' })\n\nconst props = defineProps<{ id: string }>()\n\nconst frame = inject(frameContextKey)!\nconst widget = computed(() => frame.widget(props.id))\nconst type = computed(() => widget.value && frame.type(widget.value))\nconst title = computed(() => (widget.value ? frame.title(widget.value) : ''))\nconst editing = computed(() => frame.editing())\nconst messages = computed(() => frame.messages())\n/** The filters this widget set, each with a button to clear it. */\nconst linked = computed(() => frame.linked(props.id))\n/** All of them in one tag (a pivot table's click sets one per field), cleared together. */\nconst linkLabel = computed(() => linked.value.map((link) => link.label).join(' / '))\nfunction clearLinks() {\n for (const link of linked.value) frame.clearFilter(link.key)\n}\n\n/** Others at this widget, when editing together: an outline in the first one's color and their names. */\nconst peers = computed(() => frame.peers(props.id))\nconst peerText = (peer: { name: string; action: string }) => {\n const m = messages.value\n const text =\n peer.action === 'move'\n ? m.peerMoving\n : peer.action === 'resize'\n ? m.peerResizing\n : peer.action === 'settings'\n ? m.peerSettings\n : '{name}'\n return format(text, { name: peer.name })\n}\n\nconst renaming = ref(false)\n\n// Widgets far out of view show only their title until they come near: with\n// hundreds of widgets, their contents and controls are most of the mounting.\nconst root = ref<HTMLElement>()\nconst active = ref(frame.eager(props.id))\nconst observers = inject(observersKey)\nlet stop: (() => void) | undefined\nonMounted(() => {\n if (active.value || !observers) {\n active.value = true\n return\n }\n stop = observers.near(root.value!, () => {\n active.value = true\n stop?.()\n })\n})\nonBeforeUnmount(() => stop?.())\n// the dashboard draws everything for an export only when this widget isn't drawn yet\nwatch(active, (drawn) => frame.setDrawn(props.id, drawn), { immediate: true })\nonBeforeUnmount(() => frame.setDrawn(props.id, false))\n// drawn in full to export or print the dashboard, and kept that way\nwatch(\n () => frame.all(),\n (all) => {\n if (!all || active.value) return\n active.value = true\n stop?.()\n },\n { immediate: true },\n)\nconst canFullscreen = computed(() => type.value?.fullscreen !== false)\n\nconst emitAction = (action: WidgetAction) => frame.action(props.id, action)\n\n/** The file types this widget's content can be exported as; none unless it gives a workbook. */\nconst exporters = computed(() => frame.exporters(props.id))\nconst exportError = computed(() => frame.exportError(props.id))\nconst exporting = computed(() => frame.exporting(props.id))\nconst EXPORT = 'export:'\nconst exportOpen = ref(false)\n\nfunction onSelect(value: DropdownValue | undefined) {\n if (typeof value !== 'string') return\n if (value.startsWith(EXPORT)) frame.exportTo(props.id, value.slice(EXPORT.length))\n else emitAction(value as WidgetAction)\n}\n</script>\n\n<template>\n <!-- outside the frame, which cuts off what overflows it -->\n <div v-if=\"widget && peers.length\" class=\"dou-dashboard__peers\" aria-hidden=\"true\">\n <span\n v-for=\"peer in peers\"\n :key=\"`${peer.client}:${peer.action}`\"\n :class=\"['dou-dashboard__peer', `is-${peer.action}`]\"\n :style=\"{ '--dou-dashboard-peer-color': peer.color }\"\n >\n {{ peerText(peer) }}\n </span>\n </div>\n <div\n v-if=\"widget\"\n ref=\"root\"\n :class=\"['dou-dashboard__widget', peers.length && 'has-peers']\"\n :style=\"peers.length ? { '--dou-dashboard-peer-color': peers[0]!.color } : undefined\"\n >\n <template v-if=\"!active\">\n <div class=\"dou-dashboard__header\">\n <div class=\"dou-dashboard__title\">{{ title }}</div>\n </div>\n <div class=\"dou-dashboard__body\" />\n </template>\n <component\n v-else\n :is=\"editing ? GridLayoutHandle : 'div'\"\n :as=\"editing ? 'header' : undefined\"\n class=\"dou-dashboard__header\"\n >\n <DouEditable\n v-if=\"editing\"\n v-model:editing=\"renaming\"\n class=\"dou-dashboard__title\"\n :model-value=\"title\"\n trigger=\"dblclick\"\n select-on-edit\n :aria-label=\"messages.titleField\"\n :data-dou-nodrag=\"renaming ? '' : undefined\"\n @submit=\"(value) => frame.rename(id, value)\"\n />\n <div v-else class=\"dou-dashboard__title\">{{ title }}</div>\n <div v-if=\"linked.length\" class=\"dou-dashboard__links\" data-dou-nodrag>\n <DouTag\n size=\"sm\"\n closable\n :close-label=\"format(messages.clearFilter, { value: linkLabel })\"\n @close=\"clearLinks\"\n >\n {{ format(messages.filteredBy, { value: linkLabel }) }}\n </DouTag>\n </div>\n <div class=\"dou-dashboard__actions\" data-dou-nodrag>\n <DouDropdown v-if=\"editing\" placement=\"bottom-end\" @select=\"onSelect\">\n <DouButton size=\"sm\" variant=\"ghost\" :aria-label=\"messages.widgetMenu\">\n <MoreHorizontalIcon />\n </DouButton>\n <template #menu>\n <DouDropdownItem value=\"settings\">{{ messages.settings }}</DouDropdownItem>\n <DouDropdownItem v-if=\"canFullscreen\" value=\"fullscreen\">\n {{ messages.fullscreen }}\n </DouDropdownItem>\n <DouDropdownSub v-if=\"exporters.length\" :title=\"messages.export\">\n <DouDropdownItem\n v-for=\"exporter in exporters\"\n :key=\"exporter.name\"\n :value=\"EXPORT + exporter.name\"\n :disabled=\"exporting\"\n >\n <template v-if=\"exporter.icon\" #prefix><component :is=\"exporter.icon\" /></template>\n {{ exporter.label }}\n </DouDropdownItem>\n </DouDropdownSub>\n <DouDropdownItem value=\"duplicate\" shortcut=\"mod+d\">\n {{ messages.duplicate }}\n </DouDropdownItem>\n <DouDropdownItem value=\"remove\" tone=\"danger\" shortcut=\"delete\" divided>\n {{ messages.remove }}\n </DouDropdownItem>\n </template>\n </DouDropdown>\n <DouTooltip v-if=\"!editing && exporters.length\" :content=\"messages.export\">\n <DouDropdown v-model:open=\"exportOpen\" placement=\"bottom-end\" @select=\"onSelect\">\n <DouButton\n size=\"sm\"\n variant=\"ghost\"\n :aria-label=\"messages.export\"\n :loading=\"exporting\"\n :disabled=\"exporting\"\n >\n <DownloadIcon />\n </DouButton>\n <template #menu>\n <DouDropdownItem\n v-for=\"exporter in exporters\"\n :key=\"exporter.name\"\n :value=\"EXPORT + exporter.name\"\n >\n <template v-if=\"exporter.icon\" #prefix><component :is=\"exporter.icon\" /></template>\n {{ exporter.label }}\n </DouDropdownItem>\n </template>\n </DouDropdown>\n </DouTooltip>\n <DouTooltip v-if=\"!editing && canFullscreen\" :content=\"messages.fullscreen\">\n <DouButton\n size=\"sm\"\n variant=\"ghost\"\n :aria-label=\"messages.fullscreen\"\n @click=\"emitAction('fullscreen')\"\n >\n <MaximizeIcon />\n </DouButton>\n </DouTooltip>\n </div>\n </component>\n <div v-if=\"exportError\" class=\"dou-dashboard__export-error\" data-dou-nodrag>\n <DouAlert tone=\"danger\" closable @close=\"frame.clearExportError(id)\">\n {{ exportError }}\n </DouAlert>\n </div>\n <WidgetBody\n v-if=\"active\"\n :widget=\"widget\"\n :type=\"type\"\n :editing=\"editing\"\n :messages=\"messages\"\n :set-props=\"frame.setProps\"\n :set-workbook=\"frame.setWorkbook\"\n />\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"widget-frame.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_Fragment","_renderList","_normalizeClass","_normalizeStyle","_toDisplayString","_createCommentVNode","_createElementVNode","_createBlock","_resolveDynamicComponent","GridLayoutHandle","_withCtx","_unref","_createVNode","_createTextVNode","CommentButton","_createSlots","WidgetBody"],"sources":["../../src/dashboard/widget-frame.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DownloadIcon, MaximizeIcon, MoreHorizontalIcon } from '@dou-ui/icons'\nimport {\n DouAlert,\n DouButton,\n DouDropdown,\n DouDropdownItem,\n DouDropdownSub,\n DouEditable,\n DouTag,\n DouTooltip,\n} from 'dou-ui'\nimport type { DropdownValue } from 'dou-ui'\nimport { computed, inject, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport GridLayoutHandle from '../grid/grid-handle.vue'\nimport { format } from '../locale'\nimport { frameContextKey } from './frame-context'\nimport type { WidgetAction } from './frame-context'\nimport { observersKey } from './observers'\nimport CommentButton from './comment-button.vue'\nimport WidgetBody from './widget-body.vue'\n\ndefineOptions({ name: 'DouDashboardWidget' })\n\nconst props = defineProps<{ id: string }>()\n\nconst frame = inject(frameContextKey)!\nconst widget = computed(() => frame.widget(props.id))\nconst type = computed(() => widget.value && frame.type(widget.value))\nconst title = computed(() => (widget.value ? frame.title(widget.value) : ''))\nconst editing = computed(() => frame.editing())\nconst messages = computed(() => frame.messages())\n/** The filters this widget set, each with a button to clear it. */\nconst linked = computed(() => frame.linked(props.id))\n/** All of them in one tag (a pivot table's click sets one per field), cleared together. */\nconst linkLabel = computed(() => linked.value.map((link) => link.label).join(' / '))\nfunction clearLinks() {\n for (const link of linked.value) frame.clearFilter(link.key)\n}\n\n/** Others at this widget, when editing together: an outline in the first one's color and their names. */\nconst peers = computed(() => frame.peers(props.id))\nconst peerText = (peer: { name: string; action: string }) => {\n const m = messages.value\n const text =\n peer.action === 'move'\n ? m.peerMoving\n : peer.action === 'resize'\n ? m.peerResizing\n : peer.action === 'settings'\n ? m.peerSettings\n : '{name}'\n return format(text, { name: peer.name })\n}\n\nconst renaming = ref(false)\n\n// Widgets far out of view show only their title until they come near: with\n// hundreds of widgets, their contents and controls are most of the mounting.\nconst root = ref<HTMLElement>()\nconst active = ref(frame.eager(props.id))\nconst observers = inject(observersKey)\nlet stop: (() => void) | undefined\nonMounted(() => {\n if (active.value || !observers) {\n active.value = true\n return\n }\n stop = observers.near(root.value!, () => {\n active.value = true\n stop?.()\n })\n})\nonBeforeUnmount(() => stop?.())\n// the dashboard draws everything for an export only when this widget isn't drawn yet\nwatch(active, (drawn) => frame.setDrawn(props.id, drawn), { immediate: true })\nonBeforeUnmount(() => frame.setDrawn(props.id, false))\n// drawn in full to export or print the dashboard, and kept that way\nwatch(\n () => frame.all(),\n (all) => {\n if (!all || active.value) return\n active.value = true\n stop?.()\n },\n { immediate: true },\n)\nconst canFullscreen = computed(() => type.value?.fullscreen !== false)\n\nconst emitAction = (action: WidgetAction) => frame.action(props.id, action)\n\n/** The file types this widget's content can be exported as; none unless it gives a workbook. */\nconst exporters = computed(() => frame.exporters(props.id))\nconst exportError = computed(() => frame.exportError(props.id))\nconst exporting = computed(() => frame.exporting(props.id))\nconst EXPORT = 'export:'\nconst exportOpen = ref(false)\n\nfunction onSelect(value: DropdownValue | undefined) {\n if (typeof value !== 'string') return\n if (value.startsWith(EXPORT)) frame.exportTo(props.id, value.slice(EXPORT.length))\n else emitAction(value as WidgetAction)\n}\n</script>\n\n<template>\n <!-- outside the frame, which cuts off what overflows it -->\n <div v-if=\"widget && peers.length\" class=\"dou-dashboard__peers\" aria-hidden=\"true\">\n <span\n v-for=\"peer in peers\"\n :key=\"`${peer.client}:${peer.action}`\"\n :class=\"['dou-dashboard__peer', `is-${peer.action}`]\"\n :style=\"{ '--dou-dashboard-peer-color': peer.color }\"\n >\n {{ peerText(peer) }}\n </span>\n </div>\n <div\n v-if=\"widget\"\n ref=\"root\"\n :class=\"['dou-dashboard__widget', peers.length && 'has-peers']\"\n :style=\"peers.length ? { '--dou-dashboard-peer-color': peers[0]!.color } : undefined\"\n >\n <template v-if=\"!active\">\n <div class=\"dou-dashboard__header\">\n <div class=\"dou-dashboard__title\">{{ title }}</div>\n </div>\n <div class=\"dou-dashboard__body\" />\n </template>\n <component\n v-else\n :is=\"editing ? GridLayoutHandle : 'div'\"\n :as=\"editing ? 'header' : undefined\"\n class=\"dou-dashboard__header\"\n >\n <DouEditable\n v-if=\"editing\"\n v-model:editing=\"renaming\"\n class=\"dou-dashboard__title\"\n :model-value=\"title\"\n trigger=\"dblclick\"\n select-on-edit\n :aria-label=\"messages.titleField\"\n :data-dou-nodrag=\"renaming ? '' : undefined\"\n @submit=\"(value) => frame.rename(id, value)\"\n />\n <div v-else class=\"dou-dashboard__title\">{{ title }}</div>\n <div v-if=\"linked.length\" class=\"dou-dashboard__links\" data-dou-nodrag>\n <DouTag\n size=\"sm\"\n closable\n :close-label=\"format(messages.clearFilter, { value: linkLabel })\"\n @close=\"clearLinks\"\n >\n {{ format(messages.filteredBy, { value: linkLabel }) }}\n </DouTag>\n </div>\n <div class=\"dou-dashboard__actions\" data-dou-nodrag>\n <CommentButton :id=\"id\" :comments=\"frame.comments\" :messages=\"messages\" />\n <DouDropdown v-if=\"editing\" placement=\"bottom-end\" @select=\"onSelect\">\n <DouButton size=\"sm\" variant=\"ghost\" :aria-label=\"messages.widgetMenu\">\n <MoreHorizontalIcon />\n </DouButton>\n <template #menu>\n <DouDropdownItem value=\"settings\">{{ messages.settings }}</DouDropdownItem>\n <DouDropdownItem v-if=\"canFullscreen\" value=\"fullscreen\">\n {{ messages.fullscreen }}\n </DouDropdownItem>\n <DouDropdownSub v-if=\"exporters.length\" :title=\"messages.export\">\n <DouDropdownItem\n v-for=\"exporter in exporters\"\n :key=\"exporter.name\"\n :value=\"EXPORT + exporter.name\"\n :disabled=\"exporting\"\n >\n <template v-if=\"exporter.icon\" #prefix><component :is=\"exporter.icon\" /></template>\n {{ exporter.label }}\n </DouDropdownItem>\n </DouDropdownSub>\n <DouDropdownItem value=\"duplicate\" shortcut=\"mod+d\">\n {{ messages.duplicate }}\n </DouDropdownItem>\n <DouDropdownItem value=\"remove\" tone=\"danger\" shortcut=\"delete\" divided>\n {{ messages.remove }}\n </DouDropdownItem>\n </template>\n </DouDropdown>\n <DouTooltip v-if=\"!editing && exporters.length\" :content=\"messages.export\">\n <DouDropdown v-model:open=\"exportOpen\" placement=\"bottom-end\" @select=\"onSelect\">\n <DouButton\n size=\"sm\"\n variant=\"ghost\"\n :aria-label=\"messages.export\"\n :loading=\"exporting\"\n :disabled=\"exporting\"\n >\n <DownloadIcon />\n </DouButton>\n <template #menu>\n <DouDropdownItem\n v-for=\"exporter in exporters\"\n :key=\"exporter.name\"\n :value=\"EXPORT + exporter.name\"\n >\n <template v-if=\"exporter.icon\" #prefix><component :is=\"exporter.icon\" /></template>\n {{ exporter.label }}\n </DouDropdownItem>\n </template>\n </DouDropdown>\n </DouTooltip>\n <DouTooltip v-if=\"!editing && canFullscreen\" :content=\"messages.fullscreen\">\n <DouButton\n size=\"sm\"\n variant=\"ghost\"\n :aria-label=\"messages.fullscreen\"\n @click=\"emitAction('fullscreen')\"\n >\n <MaximizeIcon />\n </DouButton>\n </DouTooltip>\n </div>\n </component>\n <div v-if=\"exportError\" class=\"dou-dashboard__export-error\" data-dou-nodrag>\n <DouAlert tone=\"danger\" closable @close=\"frame.clearExportError(id)\">\n {{ exportError }}\n </DouAlert>\n </div>\n <WidgetBody\n v-if=\"active\"\n :widget=\"widget\"\n :type=\"type\"\n :editing=\"editing\"\n :messages=\"messages\"\n :set-props=\"frame.setProps\"\n :set-workbook=\"frame.setWorkbook\"\n :report-error=\"frame.reportError\"\n />\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"widget-settings.js","names":["_sfc_main"],"sources":["../../src/dashboard/widget-settings.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport type { DashboardWidget } from '../core/types'\nimport { provideWidgetContext } from '../widget/context'\nimport type { DashboardWidgetType } from '../widget/define'\n\ndefineOptions({ name: 'DouDashboardWidgetSettings' })\n\nconst props = defineProps<{\n widget: DashboardWidget\n type: DashboardWidgetType<any>\n setProps: (id: string, patch: Record<string, unknown>, options?: { group?: string }) => void\n}>()\nconst emit = defineEmits<{ 'update:props': [props: Record<string, unknown>] }>()\n\n// the settings form can read the widget the same way its content does\nprovideWidgetContext({\n widget: () => props.widget,\n editing: () => true,\n fullscreen: () => false,\n size: { width: ref(0), height: ref(0) },\n setProps: props.setProps,\n})\n</script>\n\n<template>\n <component\n :is=\"type.settings\"\n :model-value=\"widget.props ?? {}\"\n @update:model-value=\"(value: Record<string, unknown>) => emit('update:props', value)\"\n />\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"widget-settings.js","names":["_sfc_main"],"sources":["../../src/dashboard/widget-settings.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { AlertCircleIcon } from '@dou-ui/icons'\nimport { DouButton } from 'dou-ui'\nimport { ref, watch } from 'vue'\nimport type { DashboardWidget } from '../core/types'\nimport type { DashboardMessages } from '../locale'\nimport { provideWidgetContext } from '../widget/context'\nimport type { DashboardWidgetType } from '../widget/define'\nimport { useErrorBoundary } from './error-boundary'\n\ndefineOptions({ name: 'DouDashboardWidgetSettings' })\n\nconst props = defineProps<{\n widget: DashboardWidget\n type: DashboardWidgetType<any>\n messages: DashboardMessages\n setProps: (id: string, patch: Record<string, unknown>, options?: { group?: string }) => void\n /** Told when the settings form breaks, for the dashboard's `widgetError`. */\n reportError?: (id: string, error: unknown) => void\n}>()\nconst emit = defineEmits<{ 'update:props': [props: Record<string, unknown>] }>()\n\n// the settings form can read the widget the same way its content does\nconst { data } = provideWidgetContext({\n widget: () => props.widget,\n editing: () => true,\n fullscreen: () => false,\n size: { width: ref(0), height: ref(0) },\n setProps: props.setProps,\n})\n\n// a settings form that breaks shows an error in the drawer, not to the app;\n// the title field above it still works\nconst failed = useErrorBoundary(\n () => `settings of widget \"${props.widget.id}\" (${props.widget.type})`,\n (error) => props.reportError?.(props.widget.id, error),\n)\nconst retry = () => (failed.value = false)\nwatch(() => props.widget.props, retry, { deep: true })\nwatch(data, retry)\n</script>\n\n<template>\n <div v-if=\"failed\" class=\"dou-dashboard__settings-error\" role=\"alert\">\n <AlertCircleIcon class=\"dou-dashboard__status-icon\" />\n <span>{{ messages.settingsError }}</span>\n <DouButton size=\"sm\" variant=\"soft\" @click=\"retry\">{{ messages.retry }}</DouButton>\n </div>\n <component\n :is=\"type.settings\"\n v-else\n :model-value=\"widget.props ?? {}\"\n @update:model-value=\"(value: Record<string, unknown>) => emit('update:props', value)\"\n />\n</template>\n"],"mappings":""}
@@ -1,19 +1,29 @@
1
+ import { useErrorBoundary } from "./error-boundary.js";
1
2
  import { provideWidgetContext } from "../widget/context.js";
2
- import { createBlock, defineComponent, openBlock, ref, resolveDynamicComponent } from "vue";
3
+ import { DouButton } from "dou-ui";
4
+ import { createBlock, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, openBlock, ref, resolveDynamicComponent, toDisplayString, unref, watch, withCtx } from "vue";
5
+ import { AlertCircleIcon } from "@dou-ui/icons";
3
6
  // src/dashboard/widget-settings.vue?vue&type=script&setup=true&lang.ts
7
+ var _hoisted_1 = {
8
+ key: 0,
9
+ class: "dou-dashboard__settings-error",
10
+ role: "alert"
11
+ };
4
12
  var widget_settings_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
5
13
  name: "DouDashboardWidgetSettings",
6
14
  __name: "widget-settings",
7
15
  props: {
8
16
  widget: {},
9
17
  type: {},
10
- setProps: { type: Function }
18
+ messages: {},
19
+ setProps: { type: Function },
20
+ reportError: { type: Function }
11
21
  },
12
22
  emits: ["update:props"],
13
23
  setup(__props, { emit: __emit }) {
14
24
  const props = __props;
15
25
  const emit = __emit;
16
- provideWidgetContext({
26
+ const { data } = provideWidgetContext({
17
27
  widget: () => props.widget,
18
28
  editing: () => true,
19
29
  fullscreen: () => false,
@@ -23,11 +33,27 @@ var widget_settings_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
23
33
  },
24
34
  setProps: props.setProps
25
35
  });
36
+ const failed = useErrorBoundary(() => `settings of widget "${props.widget.id}" (${props.widget.type})`, (error) => props.reportError?.(props.widget.id, error));
37
+ const retry = () => failed.value = false;
38
+ watch(() => props.widget.props, retry, { deep: true });
39
+ watch(data, retry);
26
40
  return (_ctx, _cache) => {
27
- return openBlock(), createBlock(resolveDynamicComponent(__props.type.settings), {
41
+ return unref(failed) ? (openBlock(), createElementBlock("div", _hoisted_1, [
42
+ createVNode(unref(AlertCircleIcon), { class: "dou-dashboard__status-icon" }),
43
+ createElementVNode("span", null, toDisplayString(__props.messages.settingsError), 1),
44
+ createVNode(unref(DouButton), {
45
+ size: "sm",
46
+ variant: "soft",
47
+ onClick: retry
48
+ }, {
49
+ default: withCtx(() => [createTextVNode(toDisplayString(__props.messages.retry), 1)]),
50
+ _: 1
51
+ })
52
+ ])) : (openBlock(), createBlock(resolveDynamicComponent(__props.type.settings), {
53
+ key: 1,
28
54
  "model-value": __props.widget.props ?? {},
29
55
  "onUpdate:modelValue": _cache[0] || (_cache[0] = (value) => emit("update:props", value))
30
- }, null, 8, ["model-value"]);
56
+ }, null, 8, ["model-value"]));
31
57
  };
32
58
  }
33
59
  });
@@ -1 +1 @@
1
- {"version":3,"file":"widget-settings.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_resolveDynamicComponent"],"sources":["../../src/dashboard/widget-settings.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport type { DashboardWidget } from '../core/types'\nimport { provideWidgetContext } from '../widget/context'\nimport type { DashboardWidgetType } from '../widget/define'\n\ndefineOptions({ name: 'DouDashboardWidgetSettings' })\n\nconst props = defineProps<{\n widget: DashboardWidget\n type: DashboardWidgetType<any>\n setProps: (id: string, patch: Record<string, unknown>, options?: { group?: string }) => void\n}>()\nconst emit = defineEmits<{ 'update:props': [props: Record<string, unknown>] }>()\n\n// the settings form can read the widget the same way its content does\nprovideWidgetContext({\n widget: () => props.widget,\n editing: () => true,\n fullscreen: () => false,\n size: { width: ref(0), height: ref(0) },\n setProps: props.setProps,\n})\n</script>\n\n<template>\n <component\n :is=\"type.settings\"\n :model-value=\"widget.props ?? {}\"\n @update:model-value=\"(value: Record<string, unknown>) => emit('update:props', value)\"\n />\n</template>\n"],"mappings":";;;;;;;;;;;;;EAQA,MAAM,QAAQ;EAKd,MAAM,OAAO;EAGb,qBAAqB;GACnB,cAAc,MAAM;GACpB,eAAe;GACf,kBAAkB;GAClB,MAAM;IAAE,OAAO,IAAI,CAAC;IAAG,QAAQ,IAAI,CAAC;GAAE;GACtC,UAAU,MAAM;EAClB,CAAC;;GAIC,OAAAA,UAAA,GAAAC,YAIEC,wBAHK,QAAA,KAAK,QAAQ,GAAA;IACjB,eAAa,QAAA,OAAO,SAAK,CAAA;IACzB,uBAAkB,OAAA,OAAA,OAAA,MAAG,UAAmC,KAAI,gBAAiB,KAAK"}
1
+ {"version":3,"file":"widget-settings.vue_vue_type_script_setup_true_lang.js","names":["_unref","_openBlock","_createElementBlock","_createVNode","_createElementVNode","_toDisplayString","_withCtx","_createTextVNode","_createBlock","_resolveDynamicComponent"],"sources":["../../src/dashboard/widget-settings.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { AlertCircleIcon } from '@dou-ui/icons'\nimport { DouButton } from 'dou-ui'\nimport { ref, watch } from 'vue'\nimport type { DashboardWidget } from '../core/types'\nimport type { DashboardMessages } from '../locale'\nimport { provideWidgetContext } from '../widget/context'\nimport type { DashboardWidgetType } from '../widget/define'\nimport { useErrorBoundary } from './error-boundary'\n\ndefineOptions({ name: 'DouDashboardWidgetSettings' })\n\nconst props = defineProps<{\n widget: DashboardWidget\n type: DashboardWidgetType<any>\n messages: DashboardMessages\n setProps: (id: string, patch: Record<string, unknown>, options?: { group?: string }) => void\n /** Told when the settings form breaks, for the dashboard's `widgetError`. */\n reportError?: (id: string, error: unknown) => void\n}>()\nconst emit = defineEmits<{ 'update:props': [props: Record<string, unknown>] }>()\n\n// the settings form can read the widget the same way its content does\nconst { data } = provideWidgetContext({\n widget: () => props.widget,\n editing: () => true,\n fullscreen: () => false,\n size: { width: ref(0), height: ref(0) },\n setProps: props.setProps,\n})\n\n// a settings form that breaks shows an error in the drawer, not to the app;\n// the title field above it still works\nconst failed = useErrorBoundary(\n () => `settings of widget \"${props.widget.id}\" (${props.widget.type})`,\n (error) => props.reportError?.(props.widget.id, error),\n)\nconst retry = () => (failed.value = false)\nwatch(() => props.widget.props, retry, { deep: true })\nwatch(data, retry)\n</script>\n\n<template>\n <div v-if=\"failed\" class=\"dou-dashboard__settings-error\" role=\"alert\">\n <AlertCircleIcon class=\"dou-dashboard__status-icon\" />\n <span>{{ messages.settingsError }}</span>\n <DouButton size=\"sm\" variant=\"soft\" @click=\"retry\">{{ messages.retry }}</DouButton>\n </div>\n <component\n :is=\"type.settings\"\n v-else\n :model-value=\"widget.props ?? {}\"\n @update:model-value=\"(value: Record<string, unknown>) => emit('update:props', value)\"\n />\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;EAYA,MAAM,QAAQ;EAQd,MAAM,OAAO;EAGb,MAAM,EAAE,SAAS,qBAAqB;GACpC,cAAc,MAAM;GACpB,eAAe;GACf,kBAAkB;GAClB,MAAM;IAAE,OAAO,IAAI,CAAC;IAAG,QAAQ,IAAI,CAAC;GAAE;GACtC,UAAU,MAAM;EAClB,CAAC;EAID,MAAM,SAAS,uBACP,uBAAuB,MAAM,OAAO,GAAG,KAAK,MAAM,OAAO,KAAK,KACnE,UAAU,MAAM,cAAc,MAAM,OAAO,IAAI,KAAK,CACvD;EACA,MAAM,cAAe,OAAO,QAAQ;EACpC,YAAY,MAAM,OAAO,OAAO,OAAO,EAAE,MAAM,KAAK,CAAC;EACrD,MAAM,MAAM,KAAK;;GAIJ,OAAAA,MAAA,MAAA,KAAXC,UAAA,GAAAC,mBAIM,OAJN,YAIM;IAHJC,YAAsDH,MAAA,eAAA,GAAA,EAArC,OAAM,6BAA4B,CAAA;IACnDI,mBAAyC,QAAA,MAAAC,gBAAhC,QAAA,SAAS,aAAa,GAAA,CAAA;IAC/BF,YAAmFH,MAAA,SAAA,GAAA;KAAxE,MAAK;KAAK,SAAQ;KAAQ,SAAO;;KAAO,SAAAM,cAAoB,CAAjBC,gBAAAF,gBAAA,QAAA,SAAS,KAAK,GAAA,CAAA,C;;;G,OAEtEJ,UAAA,GAAAO,YAKEC,wBAJK,QAAA,KAAK,QAAQ,GAAA;;IAEjB,eAAa,QAAA,OAAO,SAAK,CAAA;IACzB,uBAAkB,OAAA,OAAA,OAAA,MAAG,UAAmC,KAAI,gBAAiB,KAAK"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["GridLayout","GridLayoutHandle","Dashboard","DashboardPalette","DashboardFilterBar"],"sources":["../src/index.ts"],"sourcesContent":["import { withInstall } from 'dou-ui'\nimport GridLayout from './grid/grid-layout.vue'\nimport GridLayoutHandle from './grid/grid-handle.vue'\nimport Dashboard from './dashboard/dashboard.vue'\nimport DashboardPalette from './dashboard/palette.vue'\nimport DashboardFilterBar from './dashboard/filter-bar.vue'\n\nexport const DouGridLayout = withInstall(GridLayout)\nexport const DouGridLayoutHandle = withInstall(GridLayoutHandle)\nexport const DouDashboard = withInstall(Dashboard)\nexport const DouDashboardPalette = withInstall(DashboardPalette)\nexport const DouDashboardFilterBar = withInstall(DashboardFilterBar)\n\nexport type {\n DashboardAddOptions,\n DashboardEmits,\n DashboardExportErrorEvent,\n DashboardExportOptions,\n DashboardSaveOptions,\n DashboardExpose,\n DashboardFilterBarProps,\n DashboardPaletteProps,\n DashboardProps,\n} from './dashboard/types'\nexport type { DashboardDocument, DashboardPlace, DashboardWidget } from './core/types'\nexport { checkDocument, emptyDocument, isDashboardPlace, migrateDocument } from './core/document'\nexport type { CheckDocumentOptions, DocumentProblem } from './core/document'\nexport { applySteps, DashboardTransaction, replaceWidgets, sameValue } from './core/transaction'\nexport type {\n DashboardCollaboration,\n DashboardCollaborationHost,\n DashboardPeer,\n DashboardPresence,\n DashboardPresenceAction,\n} from './core/collaboration'\nexport type {\n DashboardStep,\n DashboardTransactionMeta,\n DashboardWidgetPatch,\n} from './core/transaction'\nexport { DashboardHistory } from './core/history'\nexport { defineDashboardWidget } from './widget/define'\nexport type { DashboardWidgetSize, DashboardWidgetType } from './widget/define'\nexport { useDashboardWidget } from './widget/context'\nexport { isEmptyFilter } from './data/filters'\nexport type { DashboardImageOptions } from './dashboard/export'\nexport type {\n DashboardDateRangeFilterField,\n DashboardFilterField,\n DashboardFilters,\n DashboardLinkedFilter,\n DashboardSearchFilterField,\n DashboardSelectFilterField,\n DashboardSetFilterOptions,\n DashboardSourceContext,\n DashboardSourceLoad,\n DashboardSourceOptions,\n DashboardSources,\n} from './data/types'\nexport type {\n DashboardWidgetContext,\n DashboardWidgetState,\n DashboardWidgetStatus,\n} from './widget/context'\n\nexport type {\n GridChangeEvent,\n GridChangeReason,\n GridDragEndEvent,\n GridDragEvent,\n GridDropEvent,\n GridDropItem,\n GridItemSlotProps,\n GridLayoutEmits,\n GridLayoutExpose,\n GridLayoutProps,\n GridLayouts,\n GridPlace,\n GridResizeHandle,\n} from './grid/types'\nexport {\n compactLayout,\n deriveLayout,\n findPlace,\n fitItem,\n layoutHeight,\n moveItem,\n moveItemBy,\n normalizeLayout,\n overlaps,\n resizeItem,\n resizeItemBy,\n} from './layout'\nexport type { GridCompact, GridItem, GridLayoutOptions, GridPosition, GridSize } from './layout'\nexport { dashboardLocales, useDashboardMessages } from './locale'\nexport type { DashboardMessages, DashboardMessagesOverride } from './locale'\n\ndeclare module 'vue' {\n export interface GlobalComponents {\n DouGridLayout: typeof DouGridLayout\n DouGridLayoutHandle: typeof DouGridLayoutHandle\n DouDashboard: typeof DouDashboard\n DouDashboardPalette: typeof DouDashboardPalette\n DouDashboardFilterBar: typeof DouDashboardFilterBar\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAOA,IAAa,gBAAgB,YAAYA,mBAAU;AACnD,IAAa,sBAAsB,YAAYC,mBAAgB;AAC/D,IAAa,eAAe,YAAYC,iBAAS;AACjD,IAAa,sBAAsB,YAAYC,eAAgB;AAC/D,IAAa,wBAAwB,YAAYC,kBAAkB"}
1
+ {"version":3,"file":"index.js","names":["GridLayout","GridLayoutHandle","Dashboard","DashboardPalette","DashboardFilterBar"],"sources":["../src/index.ts"],"sourcesContent":["import { withInstall } from 'dou-ui'\nimport GridLayout from './grid/grid-layout.vue'\nimport GridLayoutHandle from './grid/grid-handle.vue'\nimport Dashboard from './dashboard/dashboard.vue'\nimport DashboardPalette from './dashboard/palette.vue'\nimport DashboardFilterBar from './dashboard/filter-bar.vue'\n\nexport const DouGridLayout = withInstall(GridLayout)\nexport const DouGridLayoutHandle = withInstall(GridLayoutHandle)\nexport const DouDashboard = withInstall(Dashboard)\nexport const DouDashboardPalette = withInstall(DashboardPalette)\nexport const DouDashboardFilterBar = withInstall(DashboardFilterBar)\n\nexport type {\n DashboardAddOptions,\n DashboardEmits,\n DashboardExportErrorEvent,\n DashboardWidgetErrorEvent,\n DashboardExportOptions,\n DashboardSaveOptions,\n DashboardCommentAnchor,\n DashboardExpose,\n DashboardFilterBarProps,\n DashboardPaletteProps,\n DashboardProps,\n} from './dashboard/types'\nexport type { DashboardDocument, DashboardPlace, DashboardWidget } from './core/types'\nexport { checkDocument, emptyDocument, isDashboardPlace, migrateDocument } from './core/document'\nexport type { CheckDocumentOptions, DocumentProblem } from './core/document'\nexport { applySteps, DashboardTransaction, replaceWidgets, sameValue } from './core/transaction'\nexport type { ReplaceWidgetsOptions } from './core/transaction'\nexport type {\n DashboardCollaboration,\n DashboardCollaborationHost,\n DashboardPeer,\n DashboardPresence,\n DashboardPresenceAction,\n} from './core/collaboration'\nexport type {\n DashboardStep,\n DashboardTransactionMeta,\n DashboardWidgetPatch,\n} from './core/transaction'\nexport { DashboardHistory } from './core/history'\nexport { defineDashboardWidget } from './widget/define'\nexport type { DashboardWidgetSize, DashboardWidgetType } from './widget/define'\nexport { useDashboardWidget } from './widget/context'\nexport { isEmptyFilter } from './data/filters'\nexport type { DashboardImageOptions } from './dashboard/export'\nexport type {\n DashboardDateRangeFilterField,\n DashboardFilterField,\n DashboardFilters,\n DashboardLinkedFilter,\n DashboardSearchFilterField,\n DashboardSelectFilterField,\n DashboardSetFilterOptions,\n DashboardSourceContext,\n DashboardSourceLoad,\n DashboardSourceOptions,\n DashboardSources,\n} from './data/types'\nexport type {\n DashboardWidgetContext,\n DashboardWidgetState,\n DashboardWidgetStatus,\n} from './widget/context'\n\nexport type {\n GridChangeEvent,\n GridChangeReason,\n GridDragEndEvent,\n GridDragEvent,\n GridDropEvent,\n GridDropItem,\n GridItemSlotProps,\n GridLayoutEmits,\n GridLayoutExpose,\n GridLayoutProps,\n GridLayouts,\n GridPlace,\n GridResizeHandle,\n} from './grid/types'\nexport {\n compactLayout,\n deriveLayout,\n findPlace,\n fitItem,\n layoutHeight,\n moveItem,\n moveItemBy,\n normalizeLayout,\n overlaps,\n resizeItem,\n resizeItemBy,\n} from './layout'\nexport type { GridCompact, GridItem, GridLayoutOptions, GridPosition, GridSize } from './layout'\nexport { dashboardLocales, useDashboardMessages } from './locale'\nexport type { DashboardMessages, DashboardMessagesOverride } from './locale'\n\ndeclare module 'vue' {\n export interface GlobalComponents {\n DouGridLayout: typeof DouGridLayout\n DouGridLayoutHandle: typeof DouGridLayoutHandle\n DouDashboard: typeof DouDashboard\n DouDashboardPalette: typeof DouDashboardPalette\n DouDashboardFilterBar: typeof DouDashboardFilterBar\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAOA,IAAa,gBAAgB,YAAYA,mBAAU;AACnD,IAAa,sBAAsB,YAAYC,mBAAgB;AAC/D,IAAa,eAAe,YAAYC,iBAAS;AACjD,IAAa,sBAAsB,YAAYC,eAAgB;AAC/D,IAAa,wBAAwB,YAAYC,kBAAkB"}