@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
@@ -1 +1 @@
1
- {"version":3,"file":"chart-settings.js","names":["_sfc_main"],"sources":["../../src/chart/chart-settings.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DouInput, DouSegmentedControl, DouSwitch } from 'dou-ui'\nimport { computed } from 'vue'\nimport { useDashboardMessages } from '../locale'\nimport { useDashboardWidget } from '../widget/context'\nimport FieldSelect from '../widgets/field-select.vue'\nimport SettingsField from '../widgets/settings-field.vue'\nimport { fieldsOf, numberFieldsOf, rowsOf } from '../widgets/shared'\nimport SourceField from '../widgets/source-field.vue'\nimport type { ChartWidgetProps } from './types'\n\ndefineOptions({ name: 'DouDashboardChartSettings' })\n\nconst model = defineModel<ChartWidgetProps>({ required: true })\nconst messages = useDashboardMessages()\nconst { data } = useDashboardWidget()\nconst rows = computed(() => rowsOf(data.value, model.value.path))\nconst set = (patch: Partial<ChartWidgetProps>) => (model.value = { ...model.value, ...patch })\nconst kind = computed(() => model.value.kind ?? 'line')\n</script>\n\n<template>\n <div class=\"dou-dashboard__settings\">\n <SourceField :model-value=\"model.source\" @update:model-value=\"(source) => set({ source })\" />\n <SettingsField :label=\"messages.chartKind\">\n <DouSegmentedControl\n :model-value=\"kind\"\n :options=\"[\n { value: 'line', label: messages.chartLine },\n { value: 'area', label: messages.chartArea },\n { value: 'bar', label: messages.chartBar },\n { value: 'pie', label: messages.chartPie },\n ]\"\n :aria-label=\"messages.chartKind\"\n @update:model-value=\"(value) => set({ kind: value as ChartWidgetProps['kind'] })\"\n />\n </SettingsField>\n <FieldSelect\n :model-value=\"model.x\"\n :label=\"messages.xField\"\n :fields=\"fieldsOf(rows)\"\n @update:model-value=\"(x) => set({ x: x as string | undefined })\"\n />\n <FieldSelect\n :model-value=\"kind === 'pie' ? (model.y ?? []).slice(0, 1) : model.y\"\n :label=\"messages.yFields\"\n :fields=\"numberFieldsOf(rows)\"\n :multiple=\"kind !== 'pie'\"\n @update:model-value=\"\n (y) => set({ y: y === undefined ? undefined : Array.isArray(y) ? y : [y] })\n \"\n />\n <div v-if=\"kind === 'bar' || kind === 'area'\" class=\"dou-dashboard__field-row\">\n <DouSwitch\n :model-value=\"!!model.stacked\"\n @update:model-value=\"(stacked) => set({ stacked: stacked || undefined })\"\n >\n {{ messages.stacked }}\n </DouSwitch>\n <DouSwitch\n v-if=\"kind === 'bar'\"\n :model-value=\"!!model.horizontal\"\n @update:model-value=\"(horizontal) => set({ horizontal: horizontal || undefined })\"\n >\n {{ messages.horizontal }}\n </DouSwitch>\n </div>\n <SettingsField :label=\"messages.chartFilter\" :hint=\"messages.chartFilterHint\">\n <DouInput\n :model-value=\"model.filter ?? ''\"\n :aria-label=\"messages.chartFilter\"\n @update:model-value=\"(key) => set({ filter: key.trim() || undefined })\"\n />\n </SettingsField>\n </div>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"chart-settings.js","names":["_sfc_main"],"sources":["../../src/chart/chart-settings.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DouInput, DouSegmentedControl, DouSwitch } from 'dou-ui'\nimport { computed } from 'vue'\nimport { useDashboardMessages } from '../locale'\nimport { useDashboardWidget } from '../widget/context'\nimport FieldSelect from '../widgets/field-select.vue'\nimport SettingsField from '../widgets/settings-field.vue'\nimport { fieldsOf, numberFieldsOf, rowsOf } from '../widgets/shared'\nimport SourceField from '../widgets/source-field.vue'\nimport type { ChartWidgetProps } from './types'\nimport { chartPropsOf } from './read-props'\n\ndefineOptions({ name: 'DouDashboardChartSettings' })\n\nconst model = defineModel<ChartWidgetProps>({ required: true })\n// the props as saved may have the wrong types: read them as the widget does\nconst view = computed(() => chartPropsOf(model.value as Record<string, unknown>))\nconst messages = useDashboardMessages()\nconst { data } = useDashboardWidget()\nconst rows = computed(() => rowsOf(data.value, view.value.path))\nconst set = (patch: Partial<ChartWidgetProps>) => (model.value = { ...model.value, ...patch })\nconst kind = computed(() => view.value.kind ?? 'line')\n</script>\n\n<template>\n <div class=\"dou-dashboard__settings\">\n <SourceField :model-value=\"view.source\" @update:model-value=\"(source) => set({ source })\" />\n <SettingsField :label=\"messages.chartKind\">\n <DouSegmentedControl\n :model-value=\"kind\"\n :options=\"[\n { value: 'line', label: messages.chartLine },\n { value: 'area', label: messages.chartArea },\n { value: 'bar', label: messages.chartBar },\n { value: 'pie', label: messages.chartPie },\n ]\"\n :aria-label=\"messages.chartKind\"\n @update:model-value=\"(value) => set({ kind: value as ChartWidgetProps['kind'] })\"\n />\n </SettingsField>\n <FieldSelect\n :model-value=\"view.x\"\n :label=\"messages.xField\"\n :fields=\"fieldsOf(rows)\"\n @update:model-value=\"(x) => set({ x: x as string | undefined })\"\n />\n <FieldSelect\n :model-value=\"kind === 'pie' ? (view.y ?? []).slice(0, 1) : view.y\"\n :label=\"messages.yFields\"\n :fields=\"numberFieldsOf(rows)\"\n :multiple=\"kind !== 'pie'\"\n @update:model-value=\"\n (y) => set({ y: y === undefined ? undefined : Array.isArray(y) ? y : [y] })\n \"\n />\n <div v-if=\"kind === 'bar' || kind === 'area'\" class=\"dou-dashboard__field-row\">\n <DouSwitch\n :model-value=\"!!view.stacked\"\n @update:model-value=\"(stacked) => set({ stacked: stacked || undefined })\"\n >\n {{ messages.stacked }}\n </DouSwitch>\n <DouSwitch\n v-if=\"kind === 'bar'\"\n :model-value=\"!!view.horizontal\"\n @update:model-value=\"(horizontal) => set({ horizontal: horizontal || undefined })\"\n >\n {{ messages.horizontal }}\n </DouSwitch>\n </div>\n <SettingsField :label=\"messages.chartFilter\" :hint=\"messages.chartFilterHint\">\n <DouInput\n :model-value=\"view.filter ?? ''\"\n :aria-label=\"messages.chartFilter\"\n @update:model-value=\"(key) => set({ filter: key.trim() || undefined })\"\n />\n </SettingsField>\n </div>\n</template>\n"],"mappings":""}
@@ -4,6 +4,7 @@ import { fieldsOf, numberFieldsOf, rowsOf } from "../widgets/shared.js";
4
4
  import settings_field_default from "../widgets/settings-field.js";
5
5
  import field_select_default from "../widgets/field-select.js";
6
6
  import source_field_default from "../widgets/source-field.js";
7
+ import { chartPropsOf } from "./read-props.js";
7
8
  import { DouInput, DouSegmentedControl, DouSwitch } from "dou-ui";
8
9
  import { computed, createBlock, createCommentVNode, createElementBlock, createTextVNode, createVNode, defineComponent, openBlock, toDisplayString, unref, useModel, withCtx } from "vue";
9
10
  // src/chart/chart-settings.vue?vue&type=script&setup=true&lang.ts
@@ -22,18 +23,19 @@ var chart_settings_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
22
23
  emits: ["update:modelValue"],
23
24
  setup(__props) {
24
25
  const model = useModel(__props, "modelValue");
26
+ const view = computed(() => chartPropsOf(model.value));
25
27
  const messages = useDashboardMessages();
26
28
  const { data } = useDashboardWidget();
27
- const rows = computed(() => rowsOf(data.value, model.value.path));
29
+ const rows = computed(() => rowsOf(data.value, view.value.path));
28
30
  const set = (patch) => model.value = {
29
31
  ...model.value,
30
32
  ...patch
31
33
  };
32
- const kind = computed(() => model.value.kind ?? "line");
34
+ const kind = computed(() => view.value.kind ?? "line");
33
35
  return (_ctx, _cache) => {
34
36
  return openBlock(), createElementBlock("div", _hoisted_1, [
35
37
  createVNode(source_field_default, {
36
- "model-value": model.value.source,
38
+ "model-value": view.value.source,
37
39
  "onUpdate:modelValue": _cache[0] || (_cache[0] = (source) => set({ source }))
38
40
  }, null, 8, ["model-value"]),
39
41
  createVNode(settings_field_default, { label: unref(messages).chartKind }, {
@@ -67,7 +69,7 @@ var chart_settings_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
67
69
  _: 1
68
70
  }, 8, ["label"]),
69
71
  createVNode(field_select_default, {
70
- "model-value": model.value.x,
72
+ "model-value": view.value.x,
71
73
  label: unref(messages).xField,
72
74
  fields: unref(fieldsOf)(rows.value),
73
75
  "onUpdate:modelValue": _cache[2] || (_cache[2] = (x) => set({ x }))
@@ -77,7 +79,7 @@ var chart_settings_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
77
79
  "fields"
78
80
  ]),
79
81
  createVNode(field_select_default, {
80
- "model-value": kind.value === "pie" ? (model.value.y ?? []).slice(0, 1) : model.value.y,
82
+ "model-value": kind.value === "pie" ? (view.value.y ?? []).slice(0, 1) : view.value.y,
81
83
  label: unref(messages).yFields,
82
84
  fields: unref(numberFieldsOf)(rows.value),
83
85
  multiple: kind.value !== "pie",
@@ -89,14 +91,14 @@ var chart_settings_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
89
91
  "multiple"
90
92
  ]),
91
93
  kind.value === "bar" || kind.value === "area" ? (openBlock(), createElementBlock("div", _hoisted_2, [createVNode(unref(DouSwitch), {
92
- "model-value": !!model.value.stacked,
94
+ "model-value": !!view.value.stacked,
93
95
  "onUpdate:modelValue": _cache[4] || (_cache[4] = (stacked) => set({ stacked: stacked || void 0 }))
94
96
  }, {
95
97
  default: withCtx(() => [createTextVNode(toDisplayString(unref(messages).stacked), 1)]),
96
98
  _: 1
97
99
  }, 8, ["model-value"]), kind.value === "bar" ? (openBlock(), createBlock(unref(DouSwitch), {
98
100
  key: 0,
99
- "model-value": !!model.value.horizontal,
101
+ "model-value": !!view.value.horizontal,
100
102
  "onUpdate:modelValue": _cache[5] || (_cache[5] = (horizontal) => set({ horizontal: horizontal || void 0 }))
101
103
  }, {
102
104
  default: withCtx(() => [createTextVNode(toDisplayString(unref(messages).horizontal), 1)]),
@@ -107,7 +109,7 @@ var chart_settings_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ d
107
109
  hint: unref(messages).chartFilterHint
108
110
  }, {
109
111
  default: withCtx(() => [createVNode(unref(DouInput), {
110
- "model-value": model.value.filter ?? "",
112
+ "model-value": view.value.filter ?? "",
111
113
  "aria-label": unref(messages).chartFilter,
112
114
  "onUpdate:modelValue": _cache[6] || (_cache[6] = (key) => set({ filter: key.trim() || void 0 }))
113
115
  }, null, 8, ["model-value", "aria-label"])]),
@@ -1 +1 @@
1
- {"version":3,"file":"chart-settings.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createElementBlock","_createVNode","SourceField","SettingsField","_unref","_withCtx","FieldSelect","_createTextVNode","_toDisplayString","_createBlock","_createCommentVNode"],"sources":["../../src/chart/chart-settings.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DouInput, DouSegmentedControl, DouSwitch } from 'dou-ui'\nimport { computed } from 'vue'\nimport { useDashboardMessages } from '../locale'\nimport { useDashboardWidget } from '../widget/context'\nimport FieldSelect from '../widgets/field-select.vue'\nimport SettingsField from '../widgets/settings-field.vue'\nimport { fieldsOf, numberFieldsOf, rowsOf } from '../widgets/shared'\nimport SourceField from '../widgets/source-field.vue'\nimport type { ChartWidgetProps } from './types'\n\ndefineOptions({ name: 'DouDashboardChartSettings' })\n\nconst model = defineModel<ChartWidgetProps>({ required: true })\nconst messages = useDashboardMessages()\nconst { data } = useDashboardWidget()\nconst rows = computed(() => rowsOf(data.value, model.value.path))\nconst set = (patch: Partial<ChartWidgetProps>) => (model.value = { ...model.value, ...patch })\nconst kind = computed(() => model.value.kind ?? 'line')\n</script>\n\n<template>\n <div class=\"dou-dashboard__settings\">\n <SourceField :model-value=\"model.source\" @update:model-value=\"(source) => set({ source })\" />\n <SettingsField :label=\"messages.chartKind\">\n <DouSegmentedControl\n :model-value=\"kind\"\n :options=\"[\n { value: 'line', label: messages.chartLine },\n { value: 'area', label: messages.chartArea },\n { value: 'bar', label: messages.chartBar },\n { value: 'pie', label: messages.chartPie },\n ]\"\n :aria-label=\"messages.chartKind\"\n @update:model-value=\"(value) => set({ kind: value as ChartWidgetProps['kind'] })\"\n />\n </SettingsField>\n <FieldSelect\n :model-value=\"model.x\"\n :label=\"messages.xField\"\n :fields=\"fieldsOf(rows)\"\n @update:model-value=\"(x) => set({ x: x as string | undefined })\"\n />\n <FieldSelect\n :model-value=\"kind === 'pie' ? (model.y ?? []).slice(0, 1) : model.y\"\n :label=\"messages.yFields\"\n :fields=\"numberFieldsOf(rows)\"\n :multiple=\"kind !== 'pie'\"\n @update:model-value=\"\n (y) => set({ y: y === undefined ? undefined : Array.isArray(y) ? y : [y] })\n \"\n />\n <div v-if=\"kind === 'bar' || kind === 'area'\" class=\"dou-dashboard__field-row\">\n <DouSwitch\n :model-value=\"!!model.stacked\"\n @update:model-value=\"(stacked) => set({ stacked: stacked || undefined })\"\n >\n {{ messages.stacked }}\n </DouSwitch>\n <DouSwitch\n v-if=\"kind === 'bar'\"\n :model-value=\"!!model.horizontal\"\n @update:model-value=\"(horizontal) => set({ horizontal: horizontal || undefined })\"\n >\n {{ messages.horizontal }}\n </DouSwitch>\n </div>\n <SettingsField :label=\"messages.chartFilter\" :hint=\"messages.chartFilterHint\">\n <DouInput\n :model-value=\"model.filter ?? ''\"\n :aria-label=\"messages.chartFilter\"\n @update:model-value=\"(key) => set({ filter: key.trim() || undefined })\"\n />\n </SettingsField>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"chart-settings.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createElementBlock","_createVNode","SourceField","SettingsField","_unref","_withCtx","FieldSelect","_createTextVNode","_toDisplayString","_createBlock","_createCommentVNode"],"sources":["../../src/chart/chart-settings.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DouInput, DouSegmentedControl, DouSwitch } from 'dou-ui'\nimport { computed } from 'vue'\nimport { useDashboardMessages } from '../locale'\nimport { useDashboardWidget } from '../widget/context'\nimport FieldSelect from '../widgets/field-select.vue'\nimport SettingsField from '../widgets/settings-field.vue'\nimport { fieldsOf, numberFieldsOf, rowsOf } from '../widgets/shared'\nimport SourceField from '../widgets/source-field.vue'\nimport type { ChartWidgetProps } from './types'\nimport { chartPropsOf } from './read-props'\n\ndefineOptions({ name: 'DouDashboardChartSettings' })\n\nconst model = defineModel<ChartWidgetProps>({ required: true })\n// the props as saved may have the wrong types: read them as the widget does\nconst view = computed(() => chartPropsOf(model.value as Record<string, unknown>))\nconst messages = useDashboardMessages()\nconst { data } = useDashboardWidget()\nconst rows = computed(() => rowsOf(data.value, view.value.path))\nconst set = (patch: Partial<ChartWidgetProps>) => (model.value = { ...model.value, ...patch })\nconst kind = computed(() => view.value.kind ?? 'line')\n</script>\n\n<template>\n <div class=\"dou-dashboard__settings\">\n <SourceField :model-value=\"view.source\" @update:model-value=\"(source) => set({ source })\" />\n <SettingsField :label=\"messages.chartKind\">\n <DouSegmentedControl\n :model-value=\"kind\"\n :options=\"[\n { value: 'line', label: messages.chartLine },\n { value: 'area', label: messages.chartArea },\n { value: 'bar', label: messages.chartBar },\n { value: 'pie', label: messages.chartPie },\n ]\"\n :aria-label=\"messages.chartKind\"\n @update:model-value=\"(value) => set({ kind: value as ChartWidgetProps['kind'] })\"\n />\n </SettingsField>\n <FieldSelect\n :model-value=\"view.x\"\n :label=\"messages.xField\"\n :fields=\"fieldsOf(rows)\"\n @update:model-value=\"(x) => set({ x: x as string | undefined })\"\n />\n <FieldSelect\n :model-value=\"kind === 'pie' ? (view.y ?? []).slice(0, 1) : view.y\"\n :label=\"messages.yFields\"\n :fields=\"numberFieldsOf(rows)\"\n :multiple=\"kind !== 'pie'\"\n @update:model-value=\"\n (y) => set({ y: y === undefined ? undefined : Array.isArray(y) ? y : [y] })\n \"\n />\n <div v-if=\"kind === 'bar' || kind === 'area'\" class=\"dou-dashboard__field-row\">\n <DouSwitch\n :model-value=\"!!view.stacked\"\n @update:model-value=\"(stacked) => set({ stacked: stacked || undefined })\"\n >\n {{ messages.stacked }}\n </DouSwitch>\n <DouSwitch\n v-if=\"kind === 'bar'\"\n :model-value=\"!!view.horizontal\"\n @update:model-value=\"(horizontal) => set({ horizontal: horizontal || undefined })\"\n >\n {{ messages.horizontal }}\n </DouSwitch>\n </div>\n <SettingsField :label=\"messages.chartFilter\" :hint=\"messages.chartFilterHint\">\n <DouInput\n :model-value=\"view.filter ?? ''\"\n :aria-label=\"messages.chartFilter\"\n @update:model-value=\"(key) => set({ filter: key.trim() || undefined })\"\n />\n </SettingsField>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"chart.js","names":["_sfc_main"],"sources":["../../src/chart/chart.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n DouChart,\n DouChartArea,\n DouChartBar,\n DouChartLine,\n DouChartPie,\n DouChartTooltip,\n} from '@dou-ui/chart'\nimport type { ChartPointEvent } from '@dou-ui/chart'\nimport { computed, watch } from 'vue'\nimport { useDashboardWidget } from '../widget/context'\nimport { filterValueOf, rowsOf, rowsToWorkbook } from '../widgets/shared'\nimport type { ChartWidgetProps } from './types'\n\ndefineOptions({ name: 'DouDashboardChartWidget' })\n\nconst { props, data, size, setFilter, ownFilters, widget, setWorkbook } =\n useDashboardWidget<ChartWidgetProps>()\nconst rows = computed(() => rowsOf(data.value, props.value.path))\nconst kind = computed(() => props.value.kind ?? 'line')\nconst ys = computed(() => props.value.y ?? [])\nconst nameOf = (key: string) => props.value.labels?.[key] ?? key\n/** The x this widget filters by, if any: the other bars fade. */\nconst picked = computed(() =>\n props.value.filter ? ownFilters.value[props.value.filter] : undefined,\n)\n/** Bars other than the picked one turn gray; a new function when the pick changes, so they draw again. */\nconst barColor = computed(() => {\n const value = picked.value\n const x = props.value.x ?? ''\n if (!props.value.filter || value === undefined) return undefined\n return (row: Record<string, unknown>) =>\n filterValueOf(row[x]) === value ? undefined : 'var(--dou-color-border-strong)'\n})\n\n// the export menu writes the chart's data: a column for x, one for each series;\n// a chart without its fields has no menu\nconst seriesKeys = computed(() => (kind.value === 'pie' ? ys.value.slice(0, 1) : ys.value))\nconst exportable = computed(() => !!props.value.x && seriesKeys.value.length > 0)\nwatch(\n exportable,\n (ok) =>\n setWorkbook(\n ok\n ? () => {\n const x = props.value.x!\n return rowsToWorkbook(\n '',\n [\n { key: x, title: nameOf(x) },\n ...seriesKeys.value.map((key) => ({ key, title: nameOf(key) })),\n ],\n rows.value,\n )\n }\n : null,\n ),\n { immediate: true },\n)\n\nfunction onSelect(event: ChartPointEvent) {\n const key = props.value.filter\n if (!key) return\n const value = filterValueOf(event.x)\n if (value === picked.value) setFilter(key, undefined)\n else setFilter(key, value, { label: event.title ?? String(value) })\n}\n</script>\n\n<template>\n <DouChart\n class=\"dou-dashboard-chart\"\n :data=\"rows\"\n :x=\"props.x\"\n :height=\"size.height.value || 160\"\n :horizontal=\"kind === 'bar' && props.horizontal\"\n :legend=\"kind === 'pie' || ys.length > 1\"\n :aria-label=\"widget.title\"\n v-on=\"props.filter ? { select: onSelect } : {}\"\n >\n <DouChartPie\n v-if=\"kind === 'pie'\"\n :value=\"ys[0] ?? ''\"\n :name=\"ys[0] ? nameOf(ys[0]) : undefined\"\n :inner-radius=\"0.6\"\n />\n <template v-else>\n <template v-for=\"y in ys\" :key=\"y\">\n <DouChartBar\n v-if=\"kind === 'bar'\"\n :y=\"y\"\n :name=\"nameOf(y)\"\n :stack=\"props.stacked || undefined\"\n :color=\"barColor\"\n />\n <DouChartArea\n v-else-if=\"kind === 'area'\"\n :y=\"y\"\n :name=\"nameOf(y)\"\n :stack=\"props.stacked || undefined\"\n />\n <DouChartLine v-else :y=\"y\" :name=\"nameOf(y)\" />\n </template>\n </template>\n <DouChartTooltip />\n </DouChart>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"chart.js","names":["_sfc_main"],"sources":["../../src/chart/chart.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n DouChart,\n DouChartArea,\n DouChartBar,\n DouChartLine,\n DouChartPie,\n DouChartTooltip,\n} from '@dou-ui/chart'\nimport type { ChartPointEvent } from '@dou-ui/chart'\nimport { computed, watch } from 'vue'\nimport { useDashboardWidget } from '../widget/context'\nimport { filterValueOf, rowsOf, rowsToWorkbook } from '../widgets/shared'\nimport { chartPropsOf } from './read-props'\n\ndefineOptions({ name: 'DouDashboardChartWidget' })\n\nconst { props: raw, data, size, setFilter, ownFilters, widget, setWorkbook } = useDashboardWidget()\nconst props = computed(() => chartPropsOf(raw.value))\nconst rows = computed(() => rowsOf(data.value, props.value.path))\nconst kind = computed(() => props.value.kind ?? 'line')\nconst ys = computed(() => props.value.y ?? [])\nconst x = computed(() => props.value.x)\nconst filter = computed(() => props.value.filter)\nconst nameOf = (key: string) => props.value.labels?.[key] ?? key\n/** The x this widget filters by, if any: the other bars fade. */\nconst picked = computed(() => (filter.value ? ownFilters.value[filter.value] : undefined))\n/** Bars other than the picked one turn gray; a new function when the pick changes, so they draw again. */\nconst barColor = computed(() => {\n const value = picked.value\n const field = x.value ?? ''\n if (!filter.value || value === undefined) return undefined\n return (row: Record<string, unknown>) =>\n filterValueOf(row[field]) === value ? undefined : 'var(--dou-color-border-strong)'\n})\n\n// the export menu writes the chart's data: a column for x, one for each series;\n// a chart without its fields has no menu\nconst seriesKeys = computed(() => (kind.value === 'pie' ? ys.value.slice(0, 1) : ys.value))\nconst exportable = computed(() => !!x.value && seriesKeys.value.length > 0)\nwatch(\n exportable,\n (ok) =>\n setWorkbook(\n ok\n ? () => {\n const field = x.value!\n return rowsToWorkbook(\n '',\n [\n { key: field, title: nameOf(field) },\n ...seriesKeys.value.map((key) => ({ key, title: nameOf(key) })),\n ],\n rows.value,\n )\n }\n : null,\n ),\n { immediate: true },\n)\n\nfunction onSelect(event: ChartPointEvent) {\n const key = filter.value\n if (!key) return\n const value = filterValueOf(event.x)\n if (value === picked.value) setFilter(key, undefined)\n else setFilter(key, value, { label: event.title ?? String(value) })\n}\n</script>\n\n<template>\n <DouChart\n class=\"dou-dashboard-chart\"\n :data=\"rows\"\n :x=\"x\"\n :height=\"size.height.value || 160\"\n :horizontal=\"kind === 'bar' && props.horizontal\"\n :legend=\"kind === 'pie' || ys.length > 1\"\n :aria-label=\"widget.title\"\n v-on=\"filter ? { select: onSelect } : {}\"\n >\n <DouChartPie\n v-if=\"kind === 'pie'\"\n :value=\"ys[0] ?? ''\"\n :name=\"ys[0] ? nameOf(ys[0]) : undefined\"\n :inner-radius=\"0.6\"\n />\n <template v-else>\n <template v-for=\"y in ys\" :key=\"y\">\n <DouChartBar\n v-if=\"kind === 'bar'\"\n :y=\"y\"\n :name=\"nameOf(y)\"\n :stack=\"props.stacked || undefined\"\n :color=\"barColor\"\n />\n <DouChartArea\n v-else-if=\"kind === 'area'\"\n :y=\"y\"\n :name=\"nameOf(y)\"\n :stack=\"props.stacked || undefined\"\n />\n <DouChartLine v-else :y=\"y\" :name=\"nameOf(y)\" />\n </template>\n </template>\n <DouChartTooltip />\n </DouChart>\n</template>\n"],"mappings":""}
@@ -1,5 +1,6 @@
1
1
  import { useDashboardWidget } from "../widget/context.js";
2
2
  import { filterValueOf, rowsOf, rowsToWorkbook } from "../widgets/shared.js";
3
+ import { chartPropsOf } from "./read-props.js";
3
4
  import { Fragment, computed, createBlock, createElementBlock, createVNode, defineComponent, mergeProps, openBlock, renderList, toHandlers, unref, watch, withCtx } from "vue";
4
5
  import { DouChart, DouChartArea, DouChartBar, DouChartLine, DouChartPie, DouChartTooltip } from "@dou-ui/chart";
5
6
  // src/chart/chart.vue?vue&type=script&setup=true&lang.ts
@@ -7,34 +8,37 @@ var chart_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComp
7
8
  name: "DouDashboardChartWidget",
8
9
  __name: "chart",
9
10
  setup(__props) {
10
- const { props, data, size, setFilter, ownFilters, widget, setWorkbook } = useDashboardWidget();
11
+ const { props: raw, data, size, setFilter, ownFilters, widget, setWorkbook } = useDashboardWidget();
12
+ const props = computed(() => chartPropsOf(raw.value));
11
13
  const rows = computed(() => rowsOf(data.value, props.value.path));
12
14
  const kind = computed(() => props.value.kind ?? "line");
13
15
  const ys = computed(() => props.value.y ?? []);
16
+ const x = computed(() => props.value.x);
17
+ const filter = computed(() => props.value.filter);
14
18
  const nameOf = (key) => props.value.labels?.[key] ?? key;
15
19
  /** The x this widget filters by, if any: the other bars fade. */
16
- const picked = computed(() => props.value.filter ? ownFilters.value[props.value.filter] : void 0);
20
+ const picked = computed(() => filter.value ? ownFilters.value[filter.value] : void 0);
17
21
  /** Bars other than the picked one turn gray; a new function when the pick changes, so they draw again. */
18
22
  const barColor = computed(() => {
19
23
  const value = picked.value;
20
- const x = props.value.x ?? "";
21
- if (!props.value.filter || value === void 0) return void 0;
22
- return (row) => filterValueOf(row[x]) === value ? void 0 : "var(--dou-color-border-strong)";
24
+ const field = x.value ?? "";
25
+ if (!filter.value || value === void 0) return void 0;
26
+ return (row) => filterValueOf(row[field]) === value ? void 0 : "var(--dou-color-border-strong)";
23
27
  });
24
28
  const seriesKeys = computed(() => kind.value === "pie" ? ys.value.slice(0, 1) : ys.value);
25
- const exportable = computed(() => !!props.value.x && seriesKeys.value.length > 0);
29
+ const exportable = computed(() => !!x.value && seriesKeys.value.length > 0);
26
30
  watch(exportable, (ok) => setWorkbook(ok ? () => {
27
- const x = props.value.x;
31
+ const field = x.value;
28
32
  return rowsToWorkbook("", [{
29
- key: x,
30
- title: nameOf(x)
33
+ key: field,
34
+ title: nameOf(field)
31
35
  }, ...seriesKeys.value.map((key) => ({
32
36
  key,
33
37
  title: nameOf(key)
34
38
  }))], rows.value);
35
39
  } : null), { immediate: true });
36
40
  function onSelect(event) {
37
- const key = props.value.filter;
41
+ const key = filter.value;
38
42
  if (!key) return;
39
43
  const value = filterValueOf(event.x);
40
44
  if (value === picked.value) setFilter(key, void 0);
@@ -44,12 +48,12 @@ var chart_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComp
44
48
  return openBlock(), createBlock(unref(DouChart), mergeProps({
45
49
  class: "dou-dashboard-chart",
46
50
  data: rows.value,
47
- x: unref(props).x,
51
+ x: x.value,
48
52
  height: unref(size).height.value || 160,
49
- horizontal: kind.value === "bar" && unref(props).horizontal,
53
+ horizontal: kind.value === "bar" && props.value.horizontal,
50
54
  legend: kind.value === "pie" || ys.value.length > 1,
51
55
  "aria-label": unref(widget).title
52
- }, toHandlers(unref(props).filter ? { select: onSelect } : {})), {
56
+ }, toHandlers(filter.value ? { select: onSelect } : {})), {
53
57
  default: withCtx(() => [kind.value === "pie" ? (openBlock(), createBlock(unref(DouChartPie), {
54
58
  key: 0,
55
59
  value: ys.value[0] ?? "",
@@ -60,7 +64,7 @@ var chart_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComp
60
64
  key: 0,
61
65
  y,
62
66
  name: nameOf(y),
63
- stack: unref(props).stacked || void 0,
67
+ stack: props.value.stacked || void 0,
64
68
  color: barColor.value
65
69
  }, null, 8, [
66
70
  "y",
@@ -71,7 +75,7 @@ var chart_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComp
71
75
  key: 1,
72
76
  y,
73
77
  name: nameOf(y),
74
- stack: unref(props).stacked || void 0
78
+ stack: props.value.stacked || void 0
75
79
  }, null, 8, [
76
80
  "y",
77
81
  "name",
@@ -1 +1 @@
1
- {"version":3,"file":"chart.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_unref","_mergeProps","_toHandlers","_withCtx","_createElementBlock","_Fragment","_renderList","_createVNode"],"sources":["../../src/chart/chart.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n DouChart,\n DouChartArea,\n DouChartBar,\n DouChartLine,\n DouChartPie,\n DouChartTooltip,\n} from '@dou-ui/chart'\nimport type { ChartPointEvent } from '@dou-ui/chart'\nimport { computed, watch } from 'vue'\nimport { useDashboardWidget } from '../widget/context'\nimport { filterValueOf, rowsOf, rowsToWorkbook } from '../widgets/shared'\nimport type { ChartWidgetProps } from './types'\n\ndefineOptions({ name: 'DouDashboardChartWidget' })\n\nconst { props, data, size, setFilter, ownFilters, widget, setWorkbook } =\n useDashboardWidget<ChartWidgetProps>()\nconst rows = computed(() => rowsOf(data.value, props.value.path))\nconst kind = computed(() => props.value.kind ?? 'line')\nconst ys = computed(() => props.value.y ?? [])\nconst nameOf = (key: string) => props.value.labels?.[key] ?? key\n/** The x this widget filters by, if any: the other bars fade. */\nconst picked = computed(() =>\n props.value.filter ? ownFilters.value[props.value.filter] : undefined,\n)\n/** Bars other than the picked one turn gray; a new function when the pick changes, so they draw again. */\nconst barColor = computed(() => {\n const value = picked.value\n const x = props.value.x ?? ''\n if (!props.value.filter || value === undefined) return undefined\n return (row: Record<string, unknown>) =>\n filterValueOf(row[x]) === value ? undefined : 'var(--dou-color-border-strong)'\n})\n\n// the export menu writes the chart's data: a column for x, one for each series;\n// a chart without its fields has no menu\nconst seriesKeys = computed(() => (kind.value === 'pie' ? ys.value.slice(0, 1) : ys.value))\nconst exportable = computed(() => !!props.value.x && seriesKeys.value.length > 0)\nwatch(\n exportable,\n (ok) =>\n setWorkbook(\n ok\n ? () => {\n const x = props.value.x!\n return rowsToWorkbook(\n '',\n [\n { key: x, title: nameOf(x) },\n ...seriesKeys.value.map((key) => ({ key, title: nameOf(key) })),\n ],\n rows.value,\n )\n }\n : null,\n ),\n { immediate: true },\n)\n\nfunction onSelect(event: ChartPointEvent) {\n const key = props.value.filter\n if (!key) return\n const value = filterValueOf(event.x)\n if (value === picked.value) setFilter(key, undefined)\n else setFilter(key, value, { label: event.title ?? String(value) })\n}\n</script>\n\n<template>\n <DouChart\n class=\"dou-dashboard-chart\"\n :data=\"rows\"\n :x=\"props.x\"\n :height=\"size.height.value || 160\"\n :horizontal=\"kind === 'bar' && props.horizontal\"\n :legend=\"kind === 'pie' || ys.length > 1\"\n :aria-label=\"widget.title\"\n v-on=\"props.filter ? { select: onSelect } : {}\"\n >\n <DouChartPie\n v-if=\"kind === 'pie'\"\n :value=\"ys[0] ?? ''\"\n :name=\"ys[0] ? nameOf(ys[0]) : undefined\"\n :inner-radius=\"0.6\"\n />\n <template v-else>\n <template v-for=\"y in ys\" :key=\"y\">\n <DouChartBar\n v-if=\"kind === 'bar'\"\n :y=\"y\"\n :name=\"nameOf(y)\"\n :stack=\"props.stacked || undefined\"\n :color=\"barColor\"\n />\n <DouChartArea\n v-else-if=\"kind === 'area'\"\n :y=\"y\"\n :name=\"nameOf(y)\"\n :stack=\"props.stacked || undefined\"\n />\n <DouChartLine v-else :y=\"y\" :name=\"nameOf(y)\" />\n </template>\n </template>\n <DouChartTooltip />\n </DouChart>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"chart.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_unref","_mergeProps","_toHandlers","_withCtx","_createElementBlock","_Fragment","_renderList","_createVNode"],"sources":["../../src/chart/chart.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n DouChart,\n DouChartArea,\n DouChartBar,\n DouChartLine,\n DouChartPie,\n DouChartTooltip,\n} from '@dou-ui/chart'\nimport type { ChartPointEvent } from '@dou-ui/chart'\nimport { computed, watch } from 'vue'\nimport { useDashboardWidget } from '../widget/context'\nimport { filterValueOf, rowsOf, rowsToWorkbook } from '../widgets/shared'\nimport { chartPropsOf } from './read-props'\n\ndefineOptions({ name: 'DouDashboardChartWidget' })\n\nconst { props: raw, data, size, setFilter, ownFilters, widget, setWorkbook } = useDashboardWidget()\nconst props = computed(() => chartPropsOf(raw.value))\nconst rows = computed(() => rowsOf(data.value, props.value.path))\nconst kind = computed(() => props.value.kind ?? 'line')\nconst ys = computed(() => props.value.y ?? [])\nconst x = computed(() => props.value.x)\nconst filter = computed(() => props.value.filter)\nconst nameOf = (key: string) => props.value.labels?.[key] ?? key\n/** The x this widget filters by, if any: the other bars fade. */\nconst picked = computed(() => (filter.value ? ownFilters.value[filter.value] : undefined))\n/** Bars other than the picked one turn gray; a new function when the pick changes, so they draw again. */\nconst barColor = computed(() => {\n const value = picked.value\n const field = x.value ?? ''\n if (!filter.value || value === undefined) return undefined\n return (row: Record<string, unknown>) =>\n filterValueOf(row[field]) === value ? undefined : 'var(--dou-color-border-strong)'\n})\n\n// the export menu writes the chart's data: a column for x, one for each series;\n// a chart without its fields has no menu\nconst seriesKeys = computed(() => (kind.value === 'pie' ? ys.value.slice(0, 1) : ys.value))\nconst exportable = computed(() => !!x.value && seriesKeys.value.length > 0)\nwatch(\n exportable,\n (ok) =>\n setWorkbook(\n ok\n ? () => {\n const field = x.value!\n return rowsToWorkbook(\n '',\n [\n { key: field, title: nameOf(field) },\n ...seriesKeys.value.map((key) => ({ key, title: nameOf(key) })),\n ],\n rows.value,\n )\n }\n : null,\n ),\n { immediate: true },\n)\n\nfunction onSelect(event: ChartPointEvent) {\n const key = filter.value\n if (!key) return\n const value = filterValueOf(event.x)\n if (value === picked.value) setFilter(key, undefined)\n else setFilter(key, value, { label: event.title ?? String(value) })\n}\n</script>\n\n<template>\n <DouChart\n class=\"dou-dashboard-chart\"\n :data=\"rows\"\n :x=\"x\"\n :height=\"size.height.value || 160\"\n :horizontal=\"kind === 'bar' && props.horizontal\"\n :legend=\"kind === 'pie' || ys.length > 1\"\n :aria-label=\"widget.title\"\n v-on=\"filter ? { select: onSelect } : {}\"\n >\n <DouChartPie\n v-if=\"kind === 'pie'\"\n :value=\"ys[0] ?? ''\"\n :name=\"ys[0] ? nameOf(ys[0]) : undefined\"\n :inner-radius=\"0.6\"\n />\n <template v-else>\n <template v-for=\"y in ys\" :key=\"y\">\n <DouChartBar\n v-if=\"kind === 'bar'\"\n :y=\"y\"\n :name=\"nameOf(y)\"\n :stack=\"props.stacked || undefined\"\n :color=\"barColor\"\n />\n <DouChartArea\n v-else-if=\"kind === 'area'\"\n :y=\"y\"\n :name=\"nameOf(y)\"\n :stack=\"props.stacked || undefined\"\n />\n <DouChartLine v-else :y=\"y\" :name=\"nameOf(y)\" />\n </template>\n </template>\n <DouChartTooltip />\n </DouChart>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { flagOf, oneOf, recordOf, textOf, textsOf } from "../widgets/shared.js";
2
+ // src/chart/read-props.ts
3
+ /** The chart widget's props as its type says; what isn't the right type counts as not set. */
4
+ function chartPropsOf(props) {
5
+ const labels = {};
6
+ for (const [key, label] of Object.entries(recordOf(props.labels) ?? {})) if (typeof label === "string") labels[key] = label;
7
+ return {
8
+ source: textOf(props.source),
9
+ path: textOf(props.path),
10
+ kind: oneOf(props.kind, [
11
+ "line",
12
+ "area",
13
+ "bar",
14
+ "pie"
15
+ ]),
16
+ x: textOf(props.x),
17
+ y: textsOf(props.y),
18
+ labels,
19
+ stacked: flagOf(props.stacked),
20
+ horizontal: flagOf(props.horizontal),
21
+ filter: textOf(props.filter)
22
+ };
23
+ }
24
+ export { chartPropsOf };
25
+
26
+ //# sourceMappingURL=read-props.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-props.js","names":[],"sources":["../../src/chart/read-props.ts"],"sourcesContent":["import { flagOf, oneOf, recordOf, textOf, textsOf } from '../widgets/shared'\nimport type { ChartWidgetProps } from './types'\n\n/** The chart widget's props as its type says; what isn't the right type counts as not set. */\nexport function chartPropsOf(props: Record<string, unknown>): ChartWidgetProps {\n const labels: Record<string, string> = {}\n for (const [key, label] of Object.entries(recordOf(props.labels) ?? {}))\n if (typeof label === 'string') labels[key] = label\n return {\n source: textOf(props.source),\n path: textOf(props.path),\n kind: oneOf(props.kind, ['line', 'area', 'bar', 'pie'] as const),\n x: textOf(props.x),\n y: textsOf(props.y),\n labels,\n stacked: flagOf(props.stacked),\n horizontal: flagOf(props.horizontal),\n filter: textOf(props.filter),\n }\n}\n"],"mappings":";;;AAIA,SAAgB,aAAa,OAAkD;CAC7E,MAAM,SAAiC,CAAC;CACxC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,MAAM,MAAM,KAAK,CAAC,CAAC,GACpE,IAAI,OAAO,UAAU,UAAU,OAAO,OAAO;CAC/C,OAAO;EACL,QAAQ,OAAO,MAAM,MAAM;EAC3B,MAAM,OAAO,MAAM,IAAI;EACvB,MAAM,MAAM,MAAM,MAAM;GAAC;GAAQ;GAAQ;GAAO;EAAK,CAAU;EAC/D,GAAG,OAAO,MAAM,CAAC;EACjB,GAAG,QAAQ,MAAM,CAAC;EAClB;EACA,SAAS,OAAO,MAAM,OAAO;EAC7B,YAAY,OAAO,MAAM,UAAU;EACnC,QAAQ,OAAO,MAAM,MAAM;CAC7B;AACF"}
@@ -32,7 +32,8 @@ function shapeProblems(value, lists) {
32
32
  /**
33
33
  * The problems of a dashboard document, before it is saved or shown: an
34
34
  * unknown version, widgets without an id or sharing one, types nobody
35
- * registered, and places that are not whole cells.
35
+ * registered, places that are not whole cells, and `comments` that are
36
+ * not a list of thread ids.
36
37
  */
37
38
  function checkDocument(document, options = {}) {
38
39
  const shape = shapeProblems(document, ["widgets"]);
@@ -76,6 +77,14 @@ function checkDocument(document, options = {}) {
76
77
  text: name,
77
78
  message: `The place of "${widget.id}" on "${name}" is not whole cells`
78
79
  });
80
+ const notes = widget.comments;
81
+ if (notes !== void 0 && !(Array.isArray(notes) && notes.every((id) => typeof id === "string"))) problems.push({
82
+ code: "bad-comments",
83
+ widgetId: widget.id,
84
+ path: `${path}.comments`,
85
+ text: widget.id,
86
+ message: `The comments of "${widget.id}" are not a list of thread ids`
87
+ });
79
88
  });
80
89
  return problems;
81
90
  }
@@ -1 +1 @@
1
- {"version":3,"file":"document.js","names":[],"sources":["../../src/core/document.ts"],"sourcesContent":["import type { DashboardWidgetType } from '../widget/define'\nimport type { DashboardDocument, DashboardPlace, DashboardWidget } from './types'\n\n/** A dashboard with no widgets, the `v-model` when none is bound. */\nexport const emptyDocument = (): DashboardDocument => ({ version: 1, widgets: [] })\n\nexport interface DocumentProblem {\n code: 'version' | 'shape' | 'duplicate-id' | 'missing-id' | 'unknown-type' | 'bad-place'\n /** The widget at fault; none for the document's own problems. */\n widgetId?: string\n /** Where the problem is, e.g. `widgets[2]` or `widgets[2].place.lg`; `version` for the version. */\n path: string\n /** The id, type or breakpoint at fault, for the editor's message. */\n text: string\n /** An English description, for logs. */\n message: string\n}\n\nexport interface CheckDocumentOptions {\n /** The registered widget types; without them, types are not checked. */\n widgets?: readonly DashboardWidgetType<any>[]\n}\n\nconst isCount = (value: unknown, min: number) =>\n typeof value === 'number' && Number.isInteger(value) && value >= min\n\n/** Whether `value` is a place on a grid: whole numbers, the size at least 1. */\nexport function isDashboardPlace(value: unknown): value is DashboardPlace {\n if (!value || typeof value !== 'object') return false\n const place = value as Record<string, unknown>\n return isCount(place.x, 0) && isCount(place.y, 0) && isCount(place.w, 1) && isCount(place.h, 1)\n}\n\n/**\n * What is wrong with the shape itself: the document is not an object, or a\n * list in it is not an array. Nothing else can be checked until it is fixed.\n */\nfunction shapeProblems(value: unknown, lists: string[]): DocumentProblem[] {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return [{ code: 'shape', path: '', text: '', message: 'The document is not an object' }]\n return lists\n .filter((key) => !Array.isArray((value as Record<string, unknown>)[key]))\n .map((key) => ({ code: 'shape', path: key, text: '', message: `\"${key}\" is not a list` }))\n}\n\n/**\n * The problems of a dashboard document, before it is saved or shown: an\n * unknown version, widgets without an id or sharing one, types nobody\n * registered, and places that are not whole cells.\n */\nexport function checkDocument(\n document: DashboardDocument,\n options: CheckDocumentOptions = {},\n): DocumentProblem[] {\n const shape = shapeProblems(document, ['widgets'])\n if (shape.length) return shape\n const problems: DocumentProblem[] = []\n if (document.version !== 1)\n problems.push({\n code: 'version',\n path: 'version',\n text: String(document.version),\n message: `Version ${String(document.version)} is not known`,\n })\n const types = options.widgets && new Set(options.widgets.map((type) => type.type))\n const ids = new Set<string>()\n document.widgets.forEach((widget, i) => {\n const path = `widgets[${i}]`\n if (!widget.id) {\n problems.push({ code: 'missing-id', path, text: '', message: `${path} has no id` })\n } else if (ids.has(widget.id)) {\n problems.push({\n code: 'duplicate-id',\n widgetId: widget.id,\n path,\n text: widget.id,\n message: `The id \"${widget.id}\" is used more than once`,\n })\n }\n ids.add(widget.id)\n if (types && !types.has(widget.type))\n problems.push({\n code: 'unknown-type',\n widgetId: widget.id,\n path,\n text: widget.type,\n message: `No widget type \"${widget.type}\" is registered`,\n })\n for (const [name, place] of Object.entries(widget.place ?? {}))\n if (place !== undefined && !isDashboardPlace(place))\n problems.push({\n code: 'bad-place',\n widgetId: widget.id,\n path: `${path}.place.${name}`,\n text: name,\n message: `The place of \"${widget.id}\" on \"${name}\" is not whole cells`,\n })\n })\n return problems\n}\n\n/** The saved data as an object of version 1, or an error saying why it can't be. */\nfunction sourceOf(input: unknown): Record<string, unknown> {\n if (!input || typeof input !== 'object' || Array.isArray(input))\n throw new Error(\n `[@dou-ui/dashboard] a dashboard document must be an object, not ${Array.isArray(input) ? 'an array' : (JSON.stringify(input) ?? String(input))}`,\n )\n const source = input as Record<string, unknown>\n const version = source.version ?? 1\n if (version !== 1)\n throw new Error(\n `[@dou-ui/dashboard] unknown document version ${JSON.stringify(version) ?? String(version)} (expected 1); data saved by a newer @dou-ui/dashboard needs that version to open`,\n )\n return source\n}\n\n/**\n * Brings a saved document up to the current version, and fills what an\n * older or hand-written one may lack (`version`, `place`). Widgets that are\n * not objects and places that are not whole cells are dropped; anything\n * else is kept as it is. Throws for data that is not an object\n * (an array, `null`, a string) and for a version it does not know.\n */\nexport function migrateDocument(input: unknown): DashboardDocument {\n const source = sourceOf(input)\n const list = Array.isArray(source.widgets) ? source.widgets : []\n const widgets: DashboardWidget[] = []\n for (const value of list) {\n if (!value || typeof value !== 'object') continue\n const widget = value as Record<string, unknown>\n const place: DashboardWidget['place'] = {}\n if (widget.place && typeof widget.place === 'object')\n for (const [name, entry] of Object.entries(widget.place))\n if (isDashboardPlace(entry))\n place[name] = { x: entry.x, y: entry.y, w: entry.w, h: entry.h }\n widgets.push({\n ...widget,\n id: String(widget.id ?? ''),\n type: String(widget.type ?? ''),\n place,\n } as DashboardWidget)\n }\n return { ...source, version: 1, widgets } as DashboardDocument\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"document.js","names":[],"sources":["../../src/core/document.ts"],"sourcesContent":["import type { DashboardWidgetType } from '../widget/define'\nimport type { DashboardDocument, DashboardPlace, DashboardWidget } from './types'\n\n/** A dashboard with no widgets, the `v-model` when none is bound. */\nexport const emptyDocument = (): DashboardDocument => ({ version: 1, widgets: [] })\n\nexport interface DocumentProblem {\n code:\n | 'version'\n | 'shape'\n | 'duplicate-id'\n | 'missing-id'\n | 'unknown-type'\n | 'bad-place'\n | 'bad-comments'\n /** The widget at fault; none for the document's own problems. */\n widgetId?: string\n /** Where the problem is, e.g. `widgets[2]` or `widgets[2].place.lg`; `version` for the version. */\n path: string\n /** The id, type or breakpoint at fault, for the editor's message. */\n text: string\n /** An English description, for logs. */\n message: string\n}\n\nexport interface CheckDocumentOptions {\n /** The registered widget types; without them, types are not checked. */\n widgets?: readonly DashboardWidgetType<any>[]\n}\n\nconst isCount = (value: unknown, min: number) =>\n typeof value === 'number' && Number.isInteger(value) && value >= min\n\n/** Whether `value` is a place on a grid: whole numbers, the size at least 1. */\nexport function isDashboardPlace(value: unknown): value is DashboardPlace {\n if (!value || typeof value !== 'object') return false\n const place = value as Record<string, unknown>\n return isCount(place.x, 0) && isCount(place.y, 0) && isCount(place.w, 1) && isCount(place.h, 1)\n}\n\n/**\n * What is wrong with the shape itself: the document is not an object, or a\n * list in it is not an array. Nothing else can be checked until it is fixed.\n */\nfunction shapeProblems(value: unknown, lists: string[]): DocumentProblem[] {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return [{ code: 'shape', path: '', text: '', message: 'The document is not an object' }]\n return lists\n .filter((key) => !Array.isArray((value as Record<string, unknown>)[key]))\n .map((key) => ({ code: 'shape', path: key, text: '', message: `\"${key}\" is not a list` }))\n}\n\n/**\n * The problems of a dashboard document, before it is saved or shown: an\n * unknown version, widgets without an id or sharing one, types nobody\n * registered, places that are not whole cells, and `comments` that are\n * not a list of thread ids.\n */\nexport function checkDocument(\n document: DashboardDocument,\n options: CheckDocumentOptions = {},\n): DocumentProblem[] {\n const shape = shapeProblems(document, ['widgets'])\n if (shape.length) return shape\n const problems: DocumentProblem[] = []\n if (document.version !== 1)\n problems.push({\n code: 'version',\n path: 'version',\n text: String(document.version),\n message: `Version ${String(document.version)} is not known`,\n })\n const types = options.widgets && new Set(options.widgets.map((type) => type.type))\n const ids = new Set<string>()\n document.widgets.forEach((widget, i) => {\n const path = `widgets[${i}]`\n if (!widget.id) {\n problems.push({ code: 'missing-id', path, text: '', message: `${path} has no id` })\n } else if (ids.has(widget.id)) {\n problems.push({\n code: 'duplicate-id',\n widgetId: widget.id,\n path,\n text: widget.id,\n message: `The id \"${widget.id}\" is used more than once`,\n })\n }\n ids.add(widget.id)\n if (types && !types.has(widget.type))\n problems.push({\n code: 'unknown-type',\n widgetId: widget.id,\n path,\n text: widget.type,\n message: `No widget type \"${widget.type}\" is registered`,\n })\n for (const [name, place] of Object.entries(widget.place ?? {}))\n if (place !== undefined && !isDashboardPlace(place))\n problems.push({\n code: 'bad-place',\n widgetId: widget.id,\n path: `${path}.place.${name}`,\n text: name,\n message: `The place of \"${widget.id}\" on \"${name}\" is not whole cells`,\n })\n const notes: unknown = widget.comments\n if (\n notes !== undefined &&\n !(Array.isArray(notes) && notes.every((id) => typeof id === 'string'))\n )\n problems.push({\n code: 'bad-comments',\n widgetId: widget.id,\n path: `${path}.comments`,\n text: widget.id,\n message: `The comments of \"${widget.id}\" are not a list of thread ids`,\n })\n })\n return problems\n}\n\n/** The saved data as an object of version 1, or an error saying why it can't be. */\nfunction sourceOf(input: unknown): Record<string, unknown> {\n if (!input || typeof input !== 'object' || Array.isArray(input))\n throw new Error(\n `[@dou-ui/dashboard] a dashboard document must be an object, not ${Array.isArray(input) ? 'an array' : (JSON.stringify(input) ?? String(input))}`,\n )\n const source = input as Record<string, unknown>\n const version = source.version ?? 1\n if (version !== 1)\n throw new Error(\n `[@dou-ui/dashboard] unknown document version ${JSON.stringify(version) ?? String(version)} (expected 1); data saved by a newer @dou-ui/dashboard needs that version to open`,\n )\n return source\n}\n\n/**\n * Brings a saved document up to the current version, and fills what an\n * older or hand-written one may lack (`version`, `place`). Widgets that are\n * not objects and places that are not whole cells are dropped; anything\n * else is kept as it is. Throws for data that is not an object\n * (an array, `null`, a string) and for a version it does not know.\n */\nexport function migrateDocument(input: unknown): DashboardDocument {\n const source = sourceOf(input)\n const list = Array.isArray(source.widgets) ? source.widgets : []\n const widgets: DashboardWidget[] = []\n for (const value of list) {\n if (!value || typeof value !== 'object') continue\n const widget = value as Record<string, unknown>\n const place: DashboardWidget['place'] = {}\n if (widget.place && typeof widget.place === 'object')\n for (const [name, entry] of Object.entries(widget.place))\n if (isDashboardPlace(entry))\n place[name] = { x: entry.x, y: entry.y, w: entry.w, h: entry.h }\n widgets.push({\n ...widget,\n id: String(widget.id ?? ''),\n type: String(widget.type ?? ''),\n place,\n } as DashboardWidget)\n }\n return { ...source, version: 1, widgets } as DashboardDocument\n}\n"],"mappings":"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"}
@@ -56,11 +56,34 @@ var DashboardHistory = class {
56
56
  origin: "redo"
57
57
  });
58
58
  }
59
+ /**
60
+ * Gives a change kept out of undo to the widget as undo and redo bring it
61
+ * back, too: comments started on a widget stay on it when its adding is
62
+ * undone and redone.
63
+ */
64
+ carry(id, patch) {
65
+ const fix = (steps) => steps.map((step) => step.type === "add" && step.widget.id === id ? {
66
+ ...step,
67
+ widget: withPatch(step.widget, patch)
68
+ } : step);
69
+ for (const entry of [...this.done, ...this.undone]) {
70
+ entry.steps = fix(entry.steps);
71
+ entry.inverse = fix(entry.inverse);
72
+ }
73
+ }
59
74
  clear() {
60
75
  this.done = [];
61
76
  this.undone = [];
62
77
  }
63
78
  };
79
+ function withPatch(widget, patch) {
80
+ const next = {
81
+ ...widget,
82
+ ...patch
83
+ };
84
+ for (const [key, value] of Object.entries(patch)) if (value === void 0) delete next[key];
85
+ return next;
86
+ }
64
87
  export { DashboardHistory };
65
88
 
66
89
  //# sourceMappingURL=history.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"history.js","names":[],"sources":["../../src/core/history.ts"],"sourcesContent":["import { applySteps } from './transaction'\nimport type { DashboardStep, DashboardTransaction } from './transaction'\nimport type { DashboardDocument } from './types'\n\ninterface Entry {\n steps: DashboardStep[]\n inverse: DashboardStep[]\n group?: string\n time: number\n}\n\n/** Transactions of one group merge when they come within this many ms. */\nconst GROUP_WINDOW = 1000\n\n/** Undo and redo of a dashboard: the steps of each transaction and the steps that undo them. */\nexport class DashboardHistory {\n private done: Entry[] = []\n private undone: Entry[] = []\n\n constructor(private depth = 200) {}\n\n get canUndo() {\n return this.done.length > 0\n }\n\n get canRedo() {\n return this.undone.length > 0\n }\n\n record(tr: DashboardTransaction, now = Date.now()) {\n if (!tr.changed || tr.meta.history === false) return\n this.undone = []\n const last = this.done[this.done.length - 1]\n const group = tr.meta.group\n if (last && group && last.group === group && now - last.time < GROUP_WINDOW) {\n last.steps = [...last.steps, ...tr.steps]\n last.inverse = [...tr.inverse, ...last.inverse]\n last.time = now\n return\n }\n this.done.push({ steps: tr.steps.slice(), inverse: tr.inverse.slice(), group, time: now })\n if (this.done.length > this.depth) this.done.shift()\n }\n\n undo(document: DashboardDocument) {\n const entry = this.done.pop()\n if (!entry) return null\n this.undone.push(entry)\n return applySteps(document, entry.inverse, { history: false, origin: 'undo' })\n }\n\n redo(document: DashboardDocument) {\n const entry = this.undone.pop()\n if (!entry) return null\n this.done.push({ ...entry, group: undefined })\n return applySteps(document, entry.steps, { history: false, origin: 'redo' })\n }\n\n clear() {\n this.done = []\n this.undone = []\n }\n}\n"],"mappings":";;;AAYA,IAAM,eAAe;;AAGrB,IAAa,mBAAb,MAA8B;CAIR;CAHpB,OAAwB,CAAC;CACzB,SAA0B,CAAC;CAE3B,YAAY,QAAgB,KAAK;EAAb,KAAA,QAAA;CAAc;CAElC,IAAI,UAAU;EACZ,OAAO,KAAK,KAAK,SAAS;CAC5B;CAEA,IAAI,UAAU;EACZ,OAAO,KAAK,OAAO,SAAS;CAC9B;CAEA,OAAO,IAA0B,MAAM,KAAK,IAAI,GAAG;EACjD,IAAI,CAAC,GAAG,WAAW,GAAG,KAAK,YAAY,OAAO;EAC9C,KAAK,SAAS,CAAC;EACf,MAAM,OAAO,KAAK,KAAK,KAAK,KAAK,SAAS;EAC1C,MAAM,QAAQ,GAAG,KAAK;EACtB,IAAI,QAAQ,SAAS,KAAK,UAAU,SAAS,MAAM,KAAK,OAAO,cAAc;GAC3E,KAAK,QAAQ,CAAC,GAAG,KAAK,OAAO,GAAG,GAAG,KAAK;GACxC,KAAK,UAAU,CAAC,GAAG,GAAG,SAAS,GAAG,KAAK,OAAO;GAC9C,KAAK,OAAO;GACZ;EACF;EACA,KAAK,KAAK,KAAK;GAAE,OAAO,GAAG,MAAM,MAAM;GAAG,SAAS,GAAG,QAAQ,MAAM;GAAG;GAAO,MAAM;EAAI,CAAC;EACzF,IAAI,KAAK,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK,MAAM;CACrD;CAEA,KAAK,UAA6B;EAChC,MAAM,QAAQ,KAAK,KAAK,IAAI;EAC5B,IAAI,CAAC,OAAO,OAAO;EACnB,KAAK,OAAO,KAAK,KAAK;EACtB,OAAO,WAAW,UAAU,MAAM,SAAS;GAAE,SAAS;GAAO,QAAQ;EAAO,CAAC;CAC/E;CAEA,KAAK,UAA6B;EAChC,MAAM,QAAQ,KAAK,OAAO,IAAI;EAC9B,IAAI,CAAC,OAAO,OAAO;EACnB,KAAK,KAAK,KAAK;GAAE,GAAG;GAAO,OAAO,KAAA;EAAU,CAAC;EAC7C,OAAO,WAAW,UAAU,MAAM,OAAO;GAAE,SAAS;GAAO,QAAQ;EAAO,CAAC;CAC7E;CAEA,QAAQ;EACN,KAAK,OAAO,CAAC;EACb,KAAK,SAAS,CAAC;CACjB;AACF"}
1
+ {"version":3,"file":"history.js","names":[],"sources":["../../src/core/history.ts"],"sourcesContent":["import { applySteps } from './transaction'\nimport type { DashboardStep, DashboardTransaction } from './transaction'\nimport type { DashboardDocument, DashboardWidget } from './types'\n\ninterface Entry {\n steps: DashboardStep[]\n inverse: DashboardStep[]\n group?: string\n time: number\n}\n\n/** Transactions of one group merge when they come within this many ms. */\nconst GROUP_WINDOW = 1000\n\n/** Undo and redo of a dashboard: the steps of each transaction and the steps that undo them. */\nexport class DashboardHistory {\n private done: Entry[] = []\n private undone: Entry[] = []\n\n constructor(private depth = 200) {}\n\n get canUndo() {\n return this.done.length > 0\n }\n\n get canRedo() {\n return this.undone.length > 0\n }\n\n record(tr: DashboardTransaction, now = Date.now()) {\n if (!tr.changed || tr.meta.history === false) return\n this.undone = []\n const last = this.done[this.done.length - 1]\n const group = tr.meta.group\n if (last && group && last.group === group && now - last.time < GROUP_WINDOW) {\n last.steps = [...last.steps, ...tr.steps]\n last.inverse = [...tr.inverse, ...last.inverse]\n last.time = now\n return\n }\n this.done.push({ steps: tr.steps.slice(), inverse: tr.inverse.slice(), group, time: now })\n if (this.done.length > this.depth) this.done.shift()\n }\n\n undo(document: DashboardDocument) {\n const entry = this.done.pop()\n if (!entry) return null\n this.undone.push(entry)\n return applySteps(document, entry.inverse, { history: false, origin: 'undo' })\n }\n\n redo(document: DashboardDocument) {\n const entry = this.undone.pop()\n if (!entry) return null\n this.done.push({ ...entry, group: undefined })\n return applySteps(document, entry.steps, { history: false, origin: 'redo' })\n }\n\n /**\n * Gives a change kept out of undo to the widget as undo and redo bring it\n * back, too: comments started on a widget stay on it when its adding is\n * undone and redone.\n */\n carry(id: string, patch: Partial<DashboardWidget>) {\n const fix = (steps: DashboardStep[]) =>\n steps.map((step) =>\n step.type === 'add' && step.widget.id === id\n ? { ...step, widget: withPatch(step.widget, patch) }\n : step,\n )\n for (const entry of [...this.done, ...this.undone]) {\n entry.steps = fix(entry.steps)\n entry.inverse = fix(entry.inverse)\n }\n }\n\n clear() {\n this.done = []\n this.undone = []\n }\n}\n\nfunction withPatch(widget: DashboardWidget, patch: Partial<DashboardWidget>): DashboardWidget {\n const next = { ...widget, ...patch }\n for (const [key, value] of Object.entries(patch))\n if (value === undefined) delete (next as Record<string, unknown>)[key]\n return next\n}\n"],"mappings":";;;AAYA,IAAM,eAAe;;AAGrB,IAAa,mBAAb,MAA8B;CAIR;CAHpB,OAAwB,CAAC;CACzB,SAA0B,CAAC;CAE3B,YAAY,QAAgB,KAAK;EAAb,KAAA,QAAA;CAAc;CAElC,IAAI,UAAU;EACZ,OAAO,KAAK,KAAK,SAAS;CAC5B;CAEA,IAAI,UAAU;EACZ,OAAO,KAAK,OAAO,SAAS;CAC9B;CAEA,OAAO,IAA0B,MAAM,KAAK,IAAI,GAAG;EACjD,IAAI,CAAC,GAAG,WAAW,GAAG,KAAK,YAAY,OAAO;EAC9C,KAAK,SAAS,CAAC;EACf,MAAM,OAAO,KAAK,KAAK,KAAK,KAAK,SAAS;EAC1C,MAAM,QAAQ,GAAG,KAAK;EACtB,IAAI,QAAQ,SAAS,KAAK,UAAU,SAAS,MAAM,KAAK,OAAO,cAAc;GAC3E,KAAK,QAAQ,CAAC,GAAG,KAAK,OAAO,GAAG,GAAG,KAAK;GACxC,KAAK,UAAU,CAAC,GAAG,GAAG,SAAS,GAAG,KAAK,OAAO;GAC9C,KAAK,OAAO;GACZ;EACF;EACA,KAAK,KAAK,KAAK;GAAE,OAAO,GAAG,MAAM,MAAM;GAAG,SAAS,GAAG,QAAQ,MAAM;GAAG;GAAO,MAAM;EAAI,CAAC;EACzF,IAAI,KAAK,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK,MAAM;CACrD;CAEA,KAAK,UAA6B;EAChC,MAAM,QAAQ,KAAK,KAAK,IAAI;EAC5B,IAAI,CAAC,OAAO,OAAO;EACnB,KAAK,OAAO,KAAK,KAAK;EACtB,OAAO,WAAW,UAAU,MAAM,SAAS;GAAE,SAAS;GAAO,QAAQ;EAAO,CAAC;CAC/E;CAEA,KAAK,UAA6B;EAChC,MAAM,QAAQ,KAAK,OAAO,IAAI;EAC9B,IAAI,CAAC,OAAO,OAAO;EACnB,KAAK,KAAK,KAAK;GAAE,GAAG;GAAO,OAAO,KAAA;EAAU,CAAC;EAC7C,OAAO,WAAW,UAAU,MAAM,OAAO;GAAE,SAAS;GAAO,QAAQ;EAAO,CAAC;CAC7E;;;;;;CAOA,MAAM,IAAY,OAAiC;EACjD,MAAM,OAAO,UACX,MAAM,KAAK,SACT,KAAK,SAAS,SAAS,KAAK,OAAO,OAAO,KACtC;GAAE,GAAG;GAAM,QAAQ,UAAU,KAAK,QAAQ,KAAK;EAAE,IACjD,IACN;EACF,KAAK,MAAM,SAAS,CAAC,GAAG,KAAK,MAAM,GAAG,KAAK,MAAM,GAAG;GAClD,MAAM,QAAQ,IAAI,MAAM,KAAK;GAC7B,MAAM,UAAU,IAAI,MAAM,OAAO;EACnC;CACF;CAEA,QAAQ;EACN,KAAK,OAAO,CAAC;EACb,KAAK,SAAS,CAAC;CACjB;AACF;AAEA,SAAS,UAAU,QAAyB,OAAkD;CAC5F,MAAM,OAAO;EAAE,GAAG;EAAQ,GAAG;CAAM;CACnC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,IAAI,UAAU,KAAA,GAAW,OAAQ,KAAiC;CACpE,OAAO;AACT"}
@@ -192,8 +192,14 @@ function longestRun(values) {
192
192
  * that are gone are removed, new ones added, changed keys updated, and the
193
193
  * fewest widgets taken out and put back to match the order. Unchanged
194
194
  * widgets keep their objects.
195
+ *
196
+ * A widget that stays keeps its `comments`, the discussions on it, as they
197
+ * are: restoring a default layout doesn't take them off. Pass
198
+ * `{ keepComments: false }` for the exact steps, e.g. to apply a shared
199
+ * document.
195
200
  */
196
- function replaceWidgets(tr, next) {
201
+ function replaceWidgets(tr, next, options = {}) {
202
+ const keep = options.keepComments !== false;
197
203
  const wanted = new Map(next.widgets.map((widget, index) => [widget.id, index]));
198
204
  for (const widget of tr.doc.widgets) {
199
205
  const index = wanted.get(widget.id);
@@ -208,7 +214,7 @@ function replaceWidgets(tr, next) {
208
214
  const patch = {};
209
215
  const old = there;
210
216
  const item = widget;
211
- for (const key of /* @__PURE__ */ new Set([...Object.keys(old), ...Object.keys(item)])) if (!PROTECTED.has(key)) patch[key] = item[key];
217
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(old), ...Object.keys(item)])) if (!PROTECTED.has(key) && !(keep && key === "comments")) patch[key] = item[key];
212
218
  tr.update(widget.id, patch);
213
219
  });
214
220
  return tr;
@@ -1 +1 @@
1
- {"version":3,"file":"transaction.js","names":[],"sources":["../../src/core/transaction.ts"],"sourcesContent":["import type { DashboardDocument, DashboardWidget } from './types'\n\n/**\n * Keys of a widget to set; `undefined` removes the key. `id` and `type`\n * cannot be set: remove the widget and add another instead.\n */\nexport type DashboardWidgetPatch = { [key: string]: unknown } & {\n id?: never\n type?: never\n}\n\n/** One change to a dashboard. `index` is where in `widgets` an added widget goes. */\nexport type DashboardStep =\n | { type: 'add'; widget: DashboardWidget; index: number }\n | { type: 'remove'; id: string }\n | { type: 'update'; id: string; patch: DashboardWidgetPatch }\n\nexport interface DashboardTransactionMeta {\n /** `false` keeps the change out of undo, e.g. a change from another person. */\n history?: boolean\n /** Changes of the same group within a second become one undo step, e.g. typing in a setting. */\n group?: string\n /** Where the change came from: `layout`, `menu`, `keyboard`, `settings`, `api`… */\n origin?: string\n [key: string]: unknown\n}\n\nconst PROTECTED = new Set(['id', 'type'])\n\n/** Deep equality of JSON values; key order doesn't matter. */\nexport function sameValue(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false\n if (Array.isArray(a) !== Array.isArray(b)) return false\n const keys = Object.keys(a)\n if (keys.length !== Object.keys(b).length) return false\n return keys.every((key) =>\n sameValue((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key]),\n )\n}\n\n/** Applies one step: the new document and the step that undoes it, or `null` when it changes nothing. */\nfunction applyStep(\n document: DashboardDocument,\n step: DashboardStep,\n): [DashboardDocument, DashboardStep, DashboardStep] | null {\n const widgets = document.widgets\n const indexOf = (id: string) => widgets.findIndex((widget) => widget.id === id)\n if (step.type === 'add') {\n if (indexOf(step.widget.id) >= 0) return null\n const index = Math.max(0, Math.min(widgets.length, step.index))\n const next = [...widgets.slice(0, index), step.widget, ...widgets.slice(index)]\n return [\n { ...document, widgets: next },\n { ...step, index },\n { type: 'remove', id: step.widget.id },\n ]\n }\n const index = indexOf(step.id)\n if (index < 0) return null\n const widget = widgets[index]!\n if (step.type === 'remove') {\n const next = [...widgets.slice(0, index), ...widgets.slice(index + 1)]\n return [{ ...document, widgets: next }, step, { type: 'add', widget, index }]\n }\n const old = widget as unknown as Record<string, unknown>\n const changed: Record<string, unknown> = { ...old }\n const patch: Record<string, unknown> = {}\n const undo: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(step.patch)) {\n if (PROTECTED.has(key))\n throw new Error(`[@dou-ui/dashboard] a widget's \"${key}\" cannot be changed`)\n if (sameValue(old[key], value)) continue\n patch[key] = value\n undo[key] = old[key]\n if (value === undefined) delete changed[key]\n else changed[key] = value\n }\n if (!Object.keys(patch).length) return null\n const next = widgets.slice()\n next[index] = changed as unknown as DashboardWidget\n return [\n { ...document, widgets: next },\n { type: 'update', id: step.id, patch: patch as DashboardWidgetPatch },\n { type: 'update', id: step.id, patch: undo as DashboardWidgetPatch },\n ]\n}\n\n/**\n * Changes to a dashboard, made step by step and then handed to the\n * dashboard's `dispatch` as one change (one undo step). A step that changes\n * nothing (a widget that isn't there, a patch to the same values, an id\n * already in use) is left out.\n *\n * ```ts\n * const tr = dashboard.value.transaction()\n * tr.update('orders', { title: '本周订单' }).remove('memo')\n * dashboard.value.dispatch(tr)\n * ```\n */\nexport class DashboardTransaction {\n /** The document after the steps so far. */\n doc: DashboardDocument\n readonly steps: DashboardStep[] = []\n /** The steps that undo the ones so far, last one first. */\n readonly inverse: DashboardStep[] = []\n readonly meta: DashboardTransactionMeta = {}\n\n constructor(\n /** The document the transaction starts from. */\n readonly before: DashboardDocument,\n ) {\n this.doc = before\n }\n\n /** Whether any step was made. */\n get changed() {\n return this.steps.length > 0\n }\n\n /** Apply a step as it is. */\n step(step: DashboardStep) {\n const result = applyStep(this.doc, step)\n if (!result) return this\n this.doc = result[0]\n this.steps.push(result[1])\n this.inverse.unshift(result[2])\n return this\n }\n\n /** Put a widget into the document at `index` (the end by default). */\n add(widget: DashboardWidget, index = Infinity) {\n return this.step({ type: 'add', widget, index })\n }\n\n /** Take a widget out. */\n remove(id: string) {\n return this.step({ type: 'remove', id })\n }\n\n /** Set keys of a widget, e.g. `{ title }`, `{ props }` or `{ place }`; `undefined` removes a key. */\n update(id: string, patch: DashboardWidgetPatch) {\n return this.step({ type: 'update', id, patch })\n }\n\n setMeta(key: string, value: unknown) {\n this.meta[key] = value\n return this\n }\n}\n\n/**\n * Applies steps to a document in one go, giving the transaction: its `doc`\n * (unchanged widgets are kept as they are) and the `inverse` steps that\n * undo it. For the server, tests, or saved steps.\n */\nexport function applySteps(\n document: DashboardDocument,\n steps: readonly DashboardStep[],\n meta: DashboardTransactionMeta = {},\n): DashboardTransaction {\n const tr = new DashboardTransaction(document)\n Object.assign(tr.meta, meta)\n for (const step of steps) tr.step(step)\n return tr\n}\n\n/** A deep copy of a JSON value (also of a reactive one). */\nexport const cloneJson = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T\n\n/** The indexes of the longest strictly increasing run of `values`. */\nfunction longestRun(values: readonly number[]): Set<number> {\n const tails: number[] = []\n const previous: number[] = []\n values.forEach((value, index) => {\n let low = 0\n let high = tails.length\n while (low < high) {\n const mid = (low + high) >> 1\n if (values[tails[mid]!]! < value) low = mid + 1\n else high = mid\n }\n previous[index] = low > 0 ? tails[low - 1]! : -1\n tails[low] = index\n })\n const kept = new Set<number>()\n let index = tails.length ? tails[tails.length - 1]! : -1\n while (index >= 0) {\n kept.add(index)\n index = previous[index]!\n }\n return kept\n}\n\n/**\n * Adds the steps that turn the transaction's document into `next`: widgets\n * that are gone are removed, new ones added, changed keys updated, and the\n * fewest widgets taken out and put back to match the order. Unchanged\n * widgets keep their objects.\n */\nexport function replaceWidgets(tr: DashboardTransaction, next: DashboardDocument) {\n const wanted = new Map(next.widgets.map((widget, index) => [widget.id, index]))\n for (const widget of tr.doc.widgets) {\n const index = wanted.get(widget.id)\n // a widget whose type changed is a new widget\n if (index === undefined || next.widgets[index]!.type !== widget.type) tr.remove(widget.id)\n }\n const current = tr.doc.widgets\n const kept = longestRun(current.map((widget) => wanted.get(widget.id)!))\n for (const [index, widget] of current.entries()) if (!kept.has(index)) tr.remove(widget.id)\n next.widgets.forEach((widget, index) => {\n const there = tr.doc.widgets[index]\n if (there?.id !== widget.id) return void tr.add(widget, index)\n const patch: Record<string, unknown> = {}\n const old = there as unknown as Record<string, unknown>\n const item = widget as unknown as Record<string, unknown>\n for (const key of new Set([...Object.keys(old), ...Object.keys(item)]))\n if (!PROTECTED.has(key)) patch[key] = item[key]\n tr.update(widget.id, patch as DashboardWidgetPatch)\n })\n return tr\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"transaction.js","names":[],"sources":["../../src/core/transaction.ts"],"sourcesContent":["import type { DashboardDocument, DashboardWidget } from './types'\n\n/**\n * Keys of a widget to set; `undefined` removes the key. `id` and `type`\n * cannot be set: remove the widget and add another instead.\n */\nexport type DashboardWidgetPatch = { [key: string]: unknown } & {\n id?: never\n type?: never\n}\n\n/** One change to a dashboard. `index` is where in `widgets` an added widget goes. */\nexport type DashboardStep =\n | { type: 'add'; widget: DashboardWidget; index: number }\n | { type: 'remove'; id: string }\n | { type: 'update'; id: string; patch: DashboardWidgetPatch }\n\nexport interface DashboardTransactionMeta {\n /** `false` keeps the change out of undo, e.g. a change from another person. */\n history?: boolean\n /** Changes of the same group within a second become one undo step, e.g. typing in a setting. */\n group?: string\n /** Where the change came from: `layout`, `menu`, `keyboard`, `settings`, `api`… */\n origin?: string\n [key: string]: unknown\n}\n\nconst PROTECTED = new Set(['id', 'type'])\n\nexport interface ReplaceWidgetsOptions {\n /** A widget that stays keeps its `comments`; `true` by default. */\n keepComments?: boolean\n}\n\n/** Deep equality of JSON values; key order doesn't matter. */\nexport function sameValue(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false\n if (Array.isArray(a) !== Array.isArray(b)) return false\n const keys = Object.keys(a)\n if (keys.length !== Object.keys(b).length) return false\n return keys.every((key) =>\n sameValue((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key]),\n )\n}\n\n/** Applies one step: the new document and the step that undoes it, or `null` when it changes nothing. */\nfunction applyStep(\n document: DashboardDocument,\n step: DashboardStep,\n): [DashboardDocument, DashboardStep, DashboardStep] | null {\n const widgets = document.widgets\n const indexOf = (id: string) => widgets.findIndex((widget) => widget.id === id)\n if (step.type === 'add') {\n if (indexOf(step.widget.id) >= 0) return null\n const index = Math.max(0, Math.min(widgets.length, step.index))\n const next = [...widgets.slice(0, index), step.widget, ...widgets.slice(index)]\n return [\n { ...document, widgets: next },\n { ...step, index },\n { type: 'remove', id: step.widget.id },\n ]\n }\n const index = indexOf(step.id)\n if (index < 0) return null\n const widget = widgets[index]!\n if (step.type === 'remove') {\n const next = [...widgets.slice(0, index), ...widgets.slice(index + 1)]\n return [{ ...document, widgets: next }, step, { type: 'add', widget, index }]\n }\n const old = widget as unknown as Record<string, unknown>\n const changed: Record<string, unknown> = { ...old }\n const patch: Record<string, unknown> = {}\n const undo: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(step.patch)) {\n if (PROTECTED.has(key))\n throw new Error(`[@dou-ui/dashboard] a widget's \"${key}\" cannot be changed`)\n if (sameValue(old[key], value)) continue\n patch[key] = value\n undo[key] = old[key]\n if (value === undefined) delete changed[key]\n else changed[key] = value\n }\n if (!Object.keys(patch).length) return null\n const next = widgets.slice()\n next[index] = changed as unknown as DashboardWidget\n return [\n { ...document, widgets: next },\n { type: 'update', id: step.id, patch: patch as DashboardWidgetPatch },\n { type: 'update', id: step.id, patch: undo as DashboardWidgetPatch },\n ]\n}\n\n/**\n * Changes to a dashboard, made step by step and then handed to the\n * dashboard's `dispatch` as one change (one undo step). A step that changes\n * nothing (a widget that isn't there, a patch to the same values, an id\n * already in use) is left out.\n *\n * ```ts\n * const tr = dashboard.value.transaction()\n * tr.update('orders', { title: '本周订单' }).remove('memo')\n * dashboard.value.dispatch(tr)\n * ```\n */\nexport class DashboardTransaction {\n /** The document after the steps so far. */\n doc: DashboardDocument\n readonly steps: DashboardStep[] = []\n /** The steps that undo the ones so far, last one first. */\n readonly inverse: DashboardStep[] = []\n readonly meta: DashboardTransactionMeta = {}\n\n constructor(\n /** The document the transaction starts from. */\n readonly before: DashboardDocument,\n ) {\n this.doc = before\n }\n\n /** Whether any step was made. */\n get changed() {\n return this.steps.length > 0\n }\n\n /** Apply a step as it is. */\n step(step: DashboardStep) {\n const result = applyStep(this.doc, step)\n if (!result) return this\n this.doc = result[0]\n this.steps.push(result[1])\n this.inverse.unshift(result[2])\n return this\n }\n\n /** Put a widget into the document at `index` (the end by default). */\n add(widget: DashboardWidget, index = Infinity) {\n return this.step({ type: 'add', widget, index })\n }\n\n /** Take a widget out. */\n remove(id: string) {\n return this.step({ type: 'remove', id })\n }\n\n /** Set keys of a widget, e.g. `{ title }`, `{ props }` or `{ place }`; `undefined` removes a key. */\n update(id: string, patch: DashboardWidgetPatch) {\n return this.step({ type: 'update', id, patch })\n }\n\n setMeta(key: string, value: unknown) {\n this.meta[key] = value\n return this\n }\n}\n\n/**\n * Applies steps to a document in one go, giving the transaction: its `doc`\n * (unchanged widgets are kept as they are) and the `inverse` steps that\n * undo it. For the server, tests, or saved steps.\n */\nexport function applySteps(\n document: DashboardDocument,\n steps: readonly DashboardStep[],\n meta: DashboardTransactionMeta = {},\n): DashboardTransaction {\n const tr = new DashboardTransaction(document)\n Object.assign(tr.meta, meta)\n for (const step of steps) tr.step(step)\n return tr\n}\n\n/** A deep copy of a JSON value (also of a reactive one). */\nexport const cloneJson = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T\n\n/** The indexes of the longest strictly increasing run of `values`. */\nfunction longestRun(values: readonly number[]): Set<number> {\n const tails: number[] = []\n const previous: number[] = []\n values.forEach((value, index) => {\n let low = 0\n let high = tails.length\n while (low < high) {\n const mid = (low + high) >> 1\n if (values[tails[mid]!]! < value) low = mid + 1\n else high = mid\n }\n previous[index] = low > 0 ? tails[low - 1]! : -1\n tails[low] = index\n })\n const kept = new Set<number>()\n let index = tails.length ? tails[tails.length - 1]! : -1\n while (index >= 0) {\n kept.add(index)\n index = previous[index]!\n }\n return kept\n}\n\n/**\n * Adds the steps that turn the transaction's document into `next`: widgets\n * that are gone are removed, new ones added, changed keys updated, and the\n * fewest widgets taken out and put back to match the order. Unchanged\n * widgets keep their objects.\n *\n * A widget that stays keeps its `comments`, the discussions on it, as they\n * are: restoring a default layout doesn't take them off. Pass\n * `{ keepComments: false }` for the exact steps, e.g. to apply a shared\n * document.\n */\nexport function replaceWidgets(\n tr: DashboardTransaction,\n next: DashboardDocument,\n options: ReplaceWidgetsOptions = {},\n) {\n const keep = options.keepComments !== false\n const wanted = new Map(next.widgets.map((widget, index) => [widget.id, index]))\n for (const widget of tr.doc.widgets) {\n const index = wanted.get(widget.id)\n // a widget whose type changed is a new widget\n if (index === undefined || next.widgets[index]!.type !== widget.type) tr.remove(widget.id)\n }\n const current = tr.doc.widgets\n const kept = longestRun(current.map((widget) => wanted.get(widget.id)!))\n for (const [index, widget] of current.entries()) if (!kept.has(index)) tr.remove(widget.id)\n next.widgets.forEach((widget, index) => {\n const there = tr.doc.widgets[index]\n if (there?.id !== widget.id) return void tr.add(widget, index)\n const patch: Record<string, unknown> = {}\n const old = there as unknown as Record<string, unknown>\n const item = widget as unknown as Record<string, unknown>\n for (const key of new Set([...Object.keys(old), ...Object.keys(item)]))\n if (!PROTECTED.has(key) && !(keep && key === 'comments')) patch[key] = item[key]\n tr.update(widget.id, patch as DashboardWidgetPatch)\n })\n return tr\n}\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import comment_button_vue_vue_type_script_setup_true_lang_default from "./comment-button.vue_vue_type_script_setup_true_lang.js";
2
+ // src/dashboard/comment-button.vue
3
+ var comment_button_default = comment_button_vue_vue_type_script_setup_true_lang_default;
4
+ export { comment_button_default as default };
5
+
6
+ //# sourceMappingURL=comment-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-button.js","names":["_sfc_main"],"sources":["../../src/dashboard/comment-button.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { MessageCircleIcon } from '@dou-ui/icons'\nimport { DouButton, DouTooltip, type Placement } from 'dou-ui'\nimport { computed } from 'vue'\nimport { format, type DashboardMessages } from '../locale'\nimport type { DashboardComments } from './comments'\n\n/*\n * A widget's comment button, in its title bar and its full screen view:\n * with how many open threads there are, opening them or the box to start one.\n */\n\nconst props = withDefaults(\n defineProps<{\n id: string\n comments: DashboardComments\n messages: DashboardMessages\n /** Where the tooltip goes: in the full screen view, clear of the dialog's close button above. */\n tooltipPlacement?: Placement\n }>(),\n { tooltipPlacement: 'top' },\n)\n\nconst count = computed(() => props.comments.openOn.value.get(props.id)?.length ?? 0)\n/** Shown with something to open, or a thread to start. */\nconst shown = computed(\n () => props.comments.enabled.value && (props.comments.writable.value || count.value > 0),\n)\nconst expanded = computed(() => props.comments.current.value === props.id)\nconst label = computed(() =>\n count.value ? format(props.messages.comments, { count: count.value }) : props.messages.comment,\n)\n\nfunction toggle(event: MouseEvent) {\n if (expanded.value) return props.comments.close()\n // pressed from the keyboard (no clicks counted): straight into the reply box;\n // a click or tap only shows the threads, without bringing up a phone's keyboard\n props.comments.start(props.id, event.detail === 0)\n}\n</script>\n\n<template>\n <DouTooltip v-if=\"shown\" :content=\"label\" :placement=\"tooltipPlacement\">\n <DouButton\n size=\"sm\"\n variant=\"ghost\"\n :class=\"['dou-dashboard__comment', count && 'has-threads']\"\n :aria-label=\"label\"\n :aria-expanded=\"expanded\"\n :data-dou-comment=\"id\"\n @click=\"toggle\"\n >\n <template v-if=\"count\" #leading><MessageCircleIcon /></template>\n <template v-if=\"count\">{{ count }}</template>\n <MessageCircleIcon v-else />\n </DouButton>\n </DouTooltip>\n</template>\n"],"mappings":""}
@@ -0,0 +1,59 @@
1
+ import { format } from "../locale.js";
2
+ import { DouButton, DouTooltip } from "dou-ui";
3
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createSlots, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, toDisplayString, unref, withCtx } from "vue";
4
+ import { MessageCircleIcon } from "@dou-ui/icons";
5
+ // src/dashboard/comment-button.vue?vue&type=script&setup=true&lang.ts
6
+ var comment_button_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
7
+ __name: "comment-button",
8
+ props: {
9
+ id: {},
10
+ comments: {},
11
+ messages: {},
12
+ tooltipPlacement: { default: "top" }
13
+ },
14
+ setup(__props) {
15
+ const props = __props;
16
+ const count = computed(() => props.comments.openOn.value.get(props.id)?.length ?? 0);
17
+ /** Shown with something to open, or a thread to start. */
18
+ const shown = computed(() => props.comments.enabled.value && (props.comments.writable.value || count.value > 0));
19
+ const expanded = computed(() => props.comments.current.value === props.id);
20
+ const label = computed(() => count.value ? format(props.messages.comments, { count: count.value }) : props.messages.comment);
21
+ function toggle(event) {
22
+ if (expanded.value) return props.comments.close();
23
+ props.comments.start(props.id, event.detail === 0);
24
+ }
25
+ return (_ctx, _cache) => {
26
+ return shown.value ? (openBlock(), createBlock(unref(DouTooltip), {
27
+ key: 0,
28
+ content: label.value,
29
+ placement: __props.tooltipPlacement
30
+ }, {
31
+ default: withCtx(() => [createVNode(unref(DouButton), {
32
+ size: "sm",
33
+ variant: "ghost",
34
+ class: normalizeClass(["dou-dashboard__comment", count.value && "has-threads"]),
35
+ "aria-label": label.value,
36
+ "aria-expanded": expanded.value,
37
+ "data-dou-comment": __props.id,
38
+ onClick: toggle
39
+ }, createSlots({
40
+ default: withCtx(() => [count.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(count.value), 1)], 64)) : (openBlock(), createBlock(unref(MessageCircleIcon), { key: 1 }))]),
41
+ _: 2
42
+ }, [count.value ? {
43
+ name: "leading",
44
+ fn: withCtx(() => [createVNode(unref(MessageCircleIcon))]),
45
+ key: "0"
46
+ } : void 0]), 1032, [
47
+ "class",
48
+ "aria-label",
49
+ "aria-expanded",
50
+ "data-dou-comment"
51
+ ])]),
52
+ _: 1
53
+ }, 8, ["content", "placement"])) : createCommentVNode("", true);
54
+ };
55
+ }
56
+ });
57
+ export { comment_button_vue_vue_type_script_setup_true_lang_default as default };
58
+
59
+ //# sourceMappingURL=comment-button.vue_vue_type_script_setup_true_lang.js.map