@dou-ui/gantt 0.0.0-stage → 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -2
  3. package/dist/collaboration/index.js +323 -0
  4. package/dist/collaboration/index.js.map +1 -0
  5. package/dist/collaboration/peers.js +78 -0
  6. package/dist/collaboration/peers.js.map +1 -0
  7. package/dist/components/cell-editor.js +6 -0
  8. package/dist/components/cell-editor.js.map +1 -0
  9. package/dist/components/cell-editor.vue_vue_type_script_setup_true_lang.js +114 -0
  10. package/dist/components/cell-editor.vue_vue_type_script_setup_true_lang.js.map +1 -0
  11. package/dist/components/gantt-menu.js +6 -0
  12. package/dist/components/gantt-menu.js.map +1 -0
  13. package/dist/components/gantt-menu.vue_vue_type_script_setup_true_lang.js +40 -0
  14. package/dist/components/gantt-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  15. package/dist/components/time-grid.js +6 -0
  16. package/dist/components/time-grid.js.map +1 -0
  17. package/dist/components/time-grid.vue_vue_type_script_setup_true_lang.js +32 -0
  18. package/dist/components/time-grid.vue_vue_type_script_setup_true_lang.js.map +1 -0
  19. package/dist/components/time-header.js +6 -0
  20. package/dist/components/time-header.js.map +1 -0
  21. package/dist/components/time-header.vue_vue_type_script_setup_true_lang.js +60 -0
  22. package/dist/components/time-header.vue_vue_type_script_setup_true_lang.js.map +1 -0
  23. package/dist/context.js +12 -0
  24. package/dist/context.js.map +1 -0
  25. package/dist/core/baseline.js +48 -0
  26. package/dist/core/baseline.js.map +1 -0
  27. package/dist/core/calendar.js +191 -0
  28. package/dist/core/calendar.js.map +1 -0
  29. package/dist/core/csv.js +424 -0
  30. package/dist/core/csv.js.map +1 -0
  31. package/dist/core/document.js +231 -0
  32. package/dist/core/document.js.map +1 -0
  33. package/dist/core/extension.js +42 -0
  34. package/dist/core/extension.js.map +1 -0
  35. package/dist/core/geometry.js +112 -0
  36. package/dist/core/geometry.js.map +1 -0
  37. package/dist/core/history.js +70 -0
  38. package/dist/core/history.js.map +1 -0
  39. package/dist/core/links.js +72 -0
  40. package/dist/core/links.js.map +1 -0
  41. package/dist/core/model.js +52 -0
  42. package/dist/core/model.js.map +1 -0
  43. package/dist/core/order.js +34 -0
  44. package/dist/core/order.js.map +1 -0
  45. package/dist/core/scale.js +118 -0
  46. package/dist/core/scale.js.map +1 -0
  47. package/dist/core/schedule.js +235 -0
  48. package/dist/core/schedule.js.map +1 -0
  49. package/dist/core/time.js +81 -0
  50. package/dist/core/time.js.map +1 -0
  51. package/dist/core/transaction.js +371 -0
  52. package/dist/core/transaction.js.map +1 -0
  53. package/dist/core/tree.js +114 -0
  54. package/dist/core/tree.js.map +1 -0
  55. package/dist/export/draw.js +214 -0
  56. package/dist/export/draw.js.map +1 -0
  57. package/dist/export/theme.js +131 -0
  58. package/dist/export/theme.js.map +1 -0
  59. package/dist/gantt.js +6 -0
  60. package/dist/gantt.js.map +1 -0
  61. package/dist/gantt.vue_vue_type_script_setup_true_lang.js +2774 -0
  62. package/dist/gantt.vue_vue_type_script_setup_true_lang.js.map +1 -0
  63. package/dist/index.js +20 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/labels.js +87 -0
  66. package/dist/labels.js.map +1 -0
  67. package/dist/locale.js +355 -0
  68. package/dist/locale.js.map +1 -0
  69. package/dist/scheduler/index.js +8 -0
  70. package/dist/scheduler/index.js.map +1 -0
  71. package/dist/scheduler/layout.js +97 -0
  72. package/dist/scheduler/layout.js.map +1 -0
  73. package/dist/scheduler/scheduler.js +6 -0
  74. package/dist/scheduler/scheduler.js.map +1 -0
  75. package/dist/scheduler/scheduler.vue_vue_type_script_setup_true_lang.js +1359 -0
  76. package/dist/scheduler/scheduler.vue_vue_type_script_setup_true_lang.js.map +1 -0
  77. package/dist/scheduler/style.d.ts +1 -0
  78. package/dist/scheduler/style.js +2 -0
  79. package/dist/style.css +1 -0
  80. package/dist/style.d.ts +1 -0
  81. package/dist/style.js +4 -0
  82. package/dist/time-axis.js +337 -0
  83. package/dist/time-axis.js.map +1 -0
  84. package/dist/types/collaboration/index.d.ts +39 -0
  85. package/dist/types/collaboration/peers.d.ts +22 -0
  86. package/dist/types/components/cell-editor.vue.d.ts +21 -0
  87. package/dist/types/components/gantt-menu.vue.d.ts +18 -0
  88. package/dist/types/components/time-grid.vue.d.ts +12 -0
  89. package/dist/types/components/time-header.vue.d.ts +23 -0
  90. package/dist/types/context.d.ts +64 -0
  91. package/dist/types/core/baseline.d.ts +14 -0
  92. package/dist/types/core/calendar.d.ts +75 -0
  93. package/dist/types/core/csv.d.ts +77 -0
  94. package/dist/types/core/document.d.ts +51 -0
  95. package/dist/types/core/extension.d.ts +88 -0
  96. package/dist/types/core/geometry.d.ts +33 -0
  97. package/dist/types/core/history.d.ts +19 -0
  98. package/dist/types/core/links.d.ts +20 -0
  99. package/dist/types/core/menu.d.ts +27 -0
  100. package/dist/types/core/model.d.ts +72 -0
  101. package/dist/types/core/order.d.ts +9 -0
  102. package/dist/types/core/scale.d.ts +33 -0
  103. package/dist/types/core/schedule.d.ts +44 -0
  104. package/dist/types/core/time.d.ts +39 -0
  105. package/dist/types/core/transaction.d.ts +134 -0
  106. package/dist/types/core/tree.d.ts +30 -0
  107. package/dist/types/export/draw.d.ts +76 -0
  108. package/dist/types/export/theme.d.ts +49 -0
  109. package/dist/types/gantt.vue.d.ts +54 -0
  110. package/dist/types/index.d.ts +148 -0
  111. package/dist/types/labels.d.ts +24 -0
  112. package/dist/types/locale.d.ts +161 -0
  113. package/dist/types/scheduler/index.d.ts +102 -0
  114. package/dist/types/scheduler/layout.d.ts +58 -0
  115. package/dist/types/scheduler/scheduler.vue.d.ts +46 -0
  116. package/dist/types/scheduler/types.d.ts +109 -0
  117. package/dist/types/time-axis.d.ts +80 -0
  118. package/dist/types/types.d.ts +305 -0
  119. package/package.json +79 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gantt-menu.js","names":["_sfc_main"],"sources":["../../src/components/gantt-menu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ContextMenuItem } from 'dou-ui'\nimport { DouContextMenu } from 'dou-ui'\nimport { useTemplateRef } from 'vue'\n\ndefineProps<{ items: ContextMenuItem[] }>()\nconst open = defineModel<boolean>('open', { default: false })\n\nconst menu = useTemplateRef<InstanceType<typeof DouContextMenu>>('menu')\n\ndefineExpose({\n open: (x: number, y: number) => menu.value?.open(x, y),\n close: () => menu.value?.close(),\n})\n</script>\n\n<template>\n <!-- The chart opens the menu itself; the empty anchor only gives it a place in the page. -->\n <DouContextMenu ref=\"menu\" v-model:open=\"open\" :items=\"items\">\n <span class=\"dou-gantt__menu-anchor\" aria-hidden=\"true\" />\n </DouContextMenu>\n</template>\n"],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { DouContextMenu } from "dou-ui";
2
+ import { createBlock, createElementVNode, defineComponent, mergeModels, openBlock, unref, useModel, useTemplateRef, withCtx } from "vue";
3
+ // src/components/gantt-menu.vue?vue&type=script&setup=true&lang.ts
4
+ var gantt_menu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
5
+ __name: "gantt-menu",
6
+ props: /*@__PURE__*/ mergeModels({ items: {} }, {
7
+ "open": {
8
+ type: Boolean,
9
+ default: false
10
+ },
11
+ "openModifiers": {}
12
+ }),
13
+ emits: ["update:open"],
14
+ setup(__props, { expose: __expose }) {
15
+ const open = useModel(__props, "open");
16
+ const menu = useTemplateRef("menu");
17
+ __expose({
18
+ open: (x, y) => menu.value?.open(x, y),
19
+ close: () => menu.value?.close()
20
+ });
21
+ return (_ctx, _cache) => {
22
+ return openBlock(), createBlock(unref(DouContextMenu), {
23
+ ref_key: "menu",
24
+ ref: menu,
25
+ open: open.value,
26
+ "onUpdate:open": _cache[0] || (_cache[0] = ($event) => open.value = $event),
27
+ items: __props.items
28
+ }, {
29
+ default: withCtx(() => [..._cache[1] || (_cache[1] = [createElementVNode("span", {
30
+ class: "dou-gantt__menu-anchor",
31
+ "aria-hidden": "true"
32
+ }, null, -1)])]),
33
+ _: 1
34
+ }, 8, ["open", "items"]);
35
+ };
36
+ }
37
+ });
38
+ export { gantt_menu_vue_vue_type_script_setup_true_lang_default as default };
39
+
40
+ //# sourceMappingURL=gantt-menu.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gantt-menu.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createBlock","_unref","_withCtx","_createElementVNode"],"sources":["../../src/components/gantt-menu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ContextMenuItem } from 'dou-ui'\nimport { DouContextMenu } from 'dou-ui'\nimport { useTemplateRef } from 'vue'\n\ndefineProps<{ items: ContextMenuItem[] }>()\nconst open = defineModel<boolean>('open', { default: false })\n\nconst menu = useTemplateRef<InstanceType<typeof DouContextMenu>>('menu')\n\ndefineExpose({\n open: (x: number, y: number) => menu.value?.open(x, y),\n close: () => menu.value?.close(),\n})\n</script>\n\n<template>\n <!-- The chart opens the menu itself; the empty anchor only gives it a place in the page. -->\n <DouContextMenu ref=\"menu\" v-model:open=\"open\" :items=\"items\">\n <span class=\"dou-gantt__menu-anchor\" aria-hidden=\"true\" />\n </DouContextMenu>\n</template>\n"],"mappings":";;;;;;;;;;;;;;EAMA,MAAM,OAAOA,SAAoB,SAAC,MAA0B;EAE5D,MAAM,OAAO,eAAoD,MAAM;EAEvE,SAAa;GACX,OAAO,GAAW,MAAc,KAAK,OAAO,KAAK,GAAG,CAAC;GACrD,aAAa,KAAK,OAAO,MAAM;EACjC,CAAC;;GAKC,OAAAC,UAAA,GAAAC,YAEiBC,MAAA,cAAA,GAAA;IAFG,SAAA;IAAJ,KAAI;IAAe,MAAM,KAAA;IAAA,iBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,KAAI,QAAA;IAAG,OAAO,QAAA;;IACrD,SAAAC,cAA0D,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAA1DC,mBAA0D,QAAA;KAApD,OAAM;KAAyB,eAAY"}
@@ -0,0 +1,6 @@
1
+ import time_grid_vue_vue_type_script_setup_true_lang_default from "./time-grid.vue_vue_type_script_setup_true_lang.js";
2
+ // src/components/time-grid.vue
3
+ var time_grid_default = time_grid_vue_vue_type_script_setup_true_lang_default;
4
+ export { time_grid_default as default };
5
+
6
+ //# sourceMappingURL=time-grid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-grid.js","names":["_sfc_main"],"sources":["../../src/components/time-grid.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { HeaderCell } from '../time-axis'\n\ndefineProps<{\n cells: HeaderCell[]\n shaded: { key: number; left: number; width: number }[]\n}>()\n</script>\n\n<!-- behind the bars: days off and a line per header cell -->\n<template>\n <div\n v-for=\"range in shaded\"\n :key=\"range.key\"\n class=\"dou-gantt__off\"\n :style=\"{ left: `${range.left}px`, width: `${range.width}px` }\"\n />\n <div\n v-for=\"cell in cells\"\n :key=\"cell.key\"\n class=\"dou-gantt__gridline\"\n :style=\"{ left: `${cell.left}px` }\"\n />\n</template>\n"],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { Fragment, createElementBlock, defineComponent, normalizeStyle, openBlock, renderList } from "vue";
2
+ // src/components/time-grid.vue?vue&type=script&setup=true&lang.ts
3
+ var time_grid_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
4
+ __name: "time-grid",
5
+ props: {
6
+ cells: {},
7
+ shaded: {}
8
+ },
9
+ setup(__props) {
10
+ return (_ctx, _cache) => {
11
+ return openBlock(), createElementBlock(Fragment, null, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.shaded, (range) => {
12
+ return openBlock(), createElementBlock("div", {
13
+ key: range.key,
14
+ class: "dou-gantt__off",
15
+ style: normalizeStyle({
16
+ left: `${range.left}px`,
17
+ width: `${range.width}px`
18
+ })
19
+ }, null, 4);
20
+ }), 128)), (openBlock(true), createElementBlock(Fragment, null, renderList(__props.cells, (cell) => {
21
+ return openBlock(), createElementBlock("div", {
22
+ key: cell.key,
23
+ class: "dou-gantt__gridline",
24
+ style: normalizeStyle({ left: `${cell.left}px` })
25
+ }, null, 4);
26
+ }), 128))], 64);
27
+ };
28
+ }
29
+ });
30
+ export { time_grid_vue_vue_type_script_setup_true_lang_default as default };
31
+
32
+ //# sourceMappingURL=time-grid.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-grid.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_Fragment","_renderList","_normalizeStyle"],"sources":["../../src/components/time-grid.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { HeaderCell } from '../time-axis'\n\ndefineProps<{\n cells: HeaderCell[]\n shaded: { key: number; left: number; width: number }[]\n}>()\n</script>\n\n<!-- behind the bars: days off and a line per header cell -->\n<template>\n <div\n v-for=\"range in shaded\"\n :key=\"range.key\"\n class=\"dou-gantt__off\"\n :style=\"{ left: `${range.left}px`, width: `${range.width}px` }\"\n />\n <div\n v-for=\"cell in cells\"\n :key=\"cell.key\"\n class=\"dou-gantt__gridline\"\n :style=\"{ left: `${cell.left}px` }\"\n />\n</template>\n"],"mappings":";;;;;;;;;;G,yDAWEA,UAAA,IAAA,GAAAC,mBAKEC,UAAA,MAAAC,WAJgB,QAAA,SAAT,UAAK;IADd,OAAAH,UAAA,GAAAC,mBAKE,OAAA;KAHC,KAAK,MAAM;KACZ,OAAM;KACL,OAAKG,eAAA;MAAA,MAAA,GAAa,MAAM,KAAI;MAAA,OAAA,GAAgB,MAAM,MAAK;KAAA,CAAA;;G,YAE1DJ,UAAA,IAAA,GAAAC,mBAKEC,UAAA,MAAAC,WAJe,QAAA,QAAR,SAAI;IADb,OAAAH,UAAA,GAAAC,mBAKE,OAAA;KAHC,KAAK,KAAK;KACX,OAAM;KACL,OAAKG,eAAA,EAAA,MAAA,GAAa,KAAK,KAAI,IAAA,CAAA"}
@@ -0,0 +1,6 @@
1
+ import time_header_vue_vue_type_script_setup_true_lang_default from "./time-header.vue_vue_type_script_setup_true_lang.js";
2
+ // src/components/time-header.vue
3
+ var time_header_default = time_header_vue_vue_type_script_setup_true_lang_default;
4
+ export { time_header_default as default };
5
+
6
+ //# sourceMappingURL=time-header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-header.js","names":["_sfc_main"],"sources":["../../src/components/time-header.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { WorkCalendar } from '../core/calendar'\nimport type { TimeUnit } from '../core/scale'\nimport { dayIndex } from '../core/time'\nimport type { HeaderCell } from '../time-axis'\n\ndefineProps<{\n topCells: HeaderCell[]\n bottomCells: HeaderCell[]\n bottomUnit: TimeUnit\n calendar: WorkCalendar\n now: number\n todayX: number | null\n}>()\ndefineSlots<{ default?: () => unknown }>()\n</script>\n\n<template>\n <div class=\"dou-gantt__header\" aria-hidden=\"true\">\n <div class=\"dou-gantt__tier is-top\">\n <div\n v-for=\"cell in topCells\"\n :key=\"cell.key\"\n class=\"dou-gantt__cell\"\n :style=\"{ left: `${cell.left}px`, width: `${cell.width}px` }\"\n >\n <span class=\"dou-gantt__cell-label\">{{ cell.label }}</span>\n </div>\n </div>\n <div class=\"dou-gantt__tier is-bottom\">\n <div\n v-for=\"cell in bottomCells\"\n :key=\"cell.key\"\n class=\"dou-gantt__cell\"\n :class=\"{\n 'is-now': todayX != null && now >= cell.key && now < cell.end,\n 'is-off': bottomUnit === 'day' && !calendar.isWorkday(dayIndex(cell.key)),\n }\"\n :style=\"{ left: `${cell.left}px`, width: `${cell.width}px` }\"\n >\n <span class=\"dou-gantt__cell-label\">{{ cell.label }}</span>\n </div>\n </div>\n <div v-if=\"todayX != null\" class=\"dou-gantt__today-mark\" :style=\"{ left: `${todayX}px` }\" />\n <slot />\n </div>\n</template>\n"],"mappings":""}
@@ -0,0 +1,60 @@
1
+ import { dayIndex } from "../core/time.js";
2
+ import { Fragment, createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, renderList, renderSlot, toDisplayString, unref } from "vue";
3
+ // src/components/time-header.vue?vue&type=script&setup=true&lang.ts
4
+ var _hoisted_1 = {
5
+ class: "dou-gantt__header",
6
+ "aria-hidden": "true"
7
+ };
8
+ var _hoisted_2 = { class: "dou-gantt__tier is-top" };
9
+ var _hoisted_3 = { class: "dou-gantt__cell-label" };
10
+ var _hoisted_4 = { class: "dou-gantt__tier is-bottom" };
11
+ var _hoisted_5 = { class: "dou-gantt__cell-label" };
12
+ var time_header_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
13
+ __name: "time-header",
14
+ props: {
15
+ topCells: {},
16
+ bottomCells: {},
17
+ bottomUnit: {},
18
+ calendar: {},
19
+ now: {},
20
+ todayX: {}
21
+ },
22
+ setup(__props) {
23
+ return (_ctx, _cache) => {
24
+ return openBlock(), createElementBlock("div", _hoisted_1, [
25
+ createElementVNode("div", _hoisted_2, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.topCells, (cell) => {
26
+ return openBlock(), createElementBlock("div", {
27
+ key: cell.key,
28
+ class: "dou-gantt__cell",
29
+ style: normalizeStyle({
30
+ left: `${cell.left}px`,
31
+ width: `${cell.width}px`
32
+ })
33
+ }, [createElementVNode("span", _hoisted_3, toDisplayString(cell.label), 1)], 4);
34
+ }), 128))]),
35
+ createElementVNode("div", _hoisted_4, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.bottomCells, (cell) => {
36
+ return openBlock(), createElementBlock("div", {
37
+ key: cell.key,
38
+ class: normalizeClass(["dou-gantt__cell", {
39
+ "is-now": __props.todayX != null && __props.now >= cell.key && __props.now < cell.end,
40
+ "is-off": __props.bottomUnit === "day" && !__props.calendar.isWorkday(unref(dayIndex)(cell.key))
41
+ }]),
42
+ style: normalizeStyle({
43
+ left: `${cell.left}px`,
44
+ width: `${cell.width}px`
45
+ })
46
+ }, [createElementVNode("span", _hoisted_5, toDisplayString(cell.label), 1)], 6);
47
+ }), 128))]),
48
+ __props.todayX != null ? (openBlock(), createElementBlock("div", {
49
+ key: 0,
50
+ class: "dou-gantt__today-mark",
51
+ style: normalizeStyle({ left: `${__props.todayX}px` })
52
+ }, null, 4)) : createCommentVNode("", true),
53
+ renderSlot(_ctx.$slots, "default")
54
+ ]);
55
+ };
56
+ }
57
+ });
58
+ export { time_header_vue_vue_type_script_setup_true_lang_default as default };
59
+
60
+ //# sourceMappingURL=time-header.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-header.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_createElementVNode","_Fragment","_renderList","_normalizeStyle","_toDisplayString","_normalizeClass","_unref","_createCommentVNode","_renderSlot"],"sources":["../../src/components/time-header.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { WorkCalendar } from '../core/calendar'\nimport type { TimeUnit } from '../core/scale'\nimport { dayIndex } from '../core/time'\nimport type { HeaderCell } from '../time-axis'\n\ndefineProps<{\n topCells: HeaderCell[]\n bottomCells: HeaderCell[]\n bottomUnit: TimeUnit\n calendar: WorkCalendar\n now: number\n todayX: number | null\n}>()\ndefineSlots<{ default?: () => unknown }>()\n</script>\n\n<template>\n <div class=\"dou-gantt__header\" aria-hidden=\"true\">\n <div class=\"dou-gantt__tier is-top\">\n <div\n v-for=\"cell in topCells\"\n :key=\"cell.key\"\n class=\"dou-gantt__cell\"\n :style=\"{ left: `${cell.left}px`, width: `${cell.width}px` }\"\n >\n <span class=\"dou-gantt__cell-label\">{{ cell.label }}</span>\n </div>\n </div>\n <div class=\"dou-gantt__tier is-bottom\">\n <div\n v-for=\"cell in bottomCells\"\n :key=\"cell.key\"\n class=\"dou-gantt__cell\"\n :class=\"{\n 'is-now': todayX != null && now >= cell.key && now < cell.end,\n 'is-off': bottomUnit === 'day' && !calendar.isWorkday(dayIndex(cell.key)),\n }\"\n :style=\"{ left: `${cell.left}px`, width: `${cell.width}px` }\"\n >\n <span class=\"dou-gantt__cell-label\">{{ cell.label }}</span>\n </div>\n </div>\n <div v-if=\"todayX != null\" class=\"dou-gantt__today-mark\" :style=\"{ left: `${todayX}px` }\" />\n <slot />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;GAkBE,OAAAA,UAAA,GAAAC,mBA2BM,OA3BN,YA2BM;IA1BJC,mBASM,OATN,YASM,EARJF,UAAA,IAAA,GAAAC,mBAOME,UAAA,MAAAC,WANW,QAAA,WAAR,SAAI;KADb,OAAAJ,UAAA,GAAAC,mBAOM,OAAA;MALH,KAAK,KAAK;MACX,OAAM;MACL,OAAKI,eAAA;OAAA,MAAA,GAAa,KAAK,KAAI;OAAA,OAAA,GAAgB,KAAK,MAAK;MAAA,CAAA;K,IAEtDH,mBAA2D,QAA3D,YAA2DI,gBAApB,KAAK,KAAK,GAAA,CAAA,C;;IAGrDJ,mBAaM,OAbN,YAaM,EAZJF,UAAA,IAAA,GAAAC,mBAWME,UAAA,MAAAC,WAVW,QAAA,cAAR,SAAI;KADb,OAAAJ,UAAA,GAAAC,mBAWM,OAAA;MATH,KAAK,KAAK;MACX,OAAKM,eAAA,CAAC,mBAAiB;OACO,UAAA,QAAA,UAAM,QAAY,QAAA,OAAO,KAAK,OAAO,QAAA,MAAM,KAAK;OAAyB,UAAA,QAAA,eAAU,SAAA,CAAe,QAAA,SAAS,UAAUC,MAAA,QAAA,CAAQ,CAAC,KAAK,GAAG,CAAA;;MAInK,OAAKH,eAAA;OAAA,MAAA,GAAa,KAAK,KAAI;OAAA,OAAA,GAAgB,KAAK,MAAK;MAAA,CAAA;K,IAEtDH,mBAA2D,QAA3D,YAA2DI,gBAApB,KAAK,KAAK,GAAA,CAAA,C;;IAG1C,QAAA,UAAM,QAAjBN,UAAA,GAAAC,mBAA4F,OAAA;;KAAjE,OAAM;KAAyB,OAAKI,eAAA,EAAA,MAAA,GAAa,QAAA,OAAM,IAAA,CAAA;;IAClFK,WAAQ,KAAA,QAAA,SAAA"}
@@ -0,0 +1,12 @@
1
+ import { inject } from "vue";
2
+ // src/context.ts
3
+ var ganttKey = Symbol("dou-gantt");
4
+ /** The surrounding Gantt chart; for layers, overlays and the chart's slots. */
5
+ function useGantt() {
6
+ const context = inject(ganttKey, null);
7
+ if (!context) throw new Error("[@dou-ui/gantt] useGantt() must be called inside <DouGantt>");
8
+ return context;
9
+ }
10
+ export { ganttKey, useGantt };
11
+
12
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","names":[],"sources":["../src/context.ts"],"sourcesContent":["import { inject, type ComputedRef, type InjectionKey, type Ref } from 'vue'\nimport type { GanttLink, GanttTask } from './core/model'\nimport type { GanttCriticalPath } from './core/schedule'\nimport type { GanttScale } from './core/scale'\nimport type { GanttDocument } from './core/transaction'\nimport type { GanttRow } from './core/tree'\nimport type { GanttExpose } from './types'\nimport type { GanttMessages } from './locale'\n\n/** The surrounding Gantt chart, as `useGantt()` and extensions see it. */\nexport interface GanttContext {\n /** The whole document as it is now; change it with `transact`. */\n doc: ComputedRef<GanttDocument>\n /** The document's tasks and links, as a shortcut. */\n tasks: ComputedRef<GanttTask[]>\n links: ComputedRef<GanttLink[]>\n selection: Ref<string[]>\n /** The task the keyboard is on. */\n active: Ref<string | null>\n readonly: ComputedRef<boolean>\n messages: ComputedRef<GanttMessages>\n scale: ComputedRef<GanttScale>\n /** How wide a day is, in px. */\n dayWidth: Ref<number>\n /** Height of a row, in px. */\n rowHeight: ComputedRef<number>\n /** The rows as laid out, with spans and summaries worked out. */\n rows: ComputedRef<GanttRow[]>\n /**\n * While a bar is dragged: the tasks it would change, with the ones it\n * would push, as they would be written. `null` otherwise.\n */\n preview: ComputedRef<Map<string, Partial<GanttTask>> | null>\n transact: GanttExpose['transact']\n /** The critical path when `critical` is on, `null` otherwise. */\n critical: ComputedRef<GanttCriticalPath | null>\n /** Links not kept by the dates as they are. */\n brokenLinks: ComputedRef<Set<string>>\n schedule: GanttExpose['schedule']\n editCell: GanttExpose['editCell']\n insertTask: GanttExpose['insertTask']\n indentTask: GanttExpose['indentTask']\n outdentTask: GanttExpose['outdentTask']\n undo: () => boolean\n redo: () => boolean\n canUndo: Ref<boolean>\n canRedo: Ref<boolean>\n fit: () => void\n zoomIn: () => void\n zoomOut: () => void\n scrollToDate: GanttExpose['scrollToDate']\n scrollToTask: GanttExpose['scrollToTask']\n scrollToToday: () => void\n /** Puts the keyboard on a task, scrolls it into view and focuses its bar. */\n focusTask: (id: string) => void\n /** Where a time (a date string or minutes) is on the timeline, in px from its start. */\n xOf: (time: string | number) => number | null\n /** Where a task's row is on the timeline, in px from the top of the rows. */\n rowTop: (id: string) => number | null\n /** Says `text` to screen readers. */\n announce: (text: string) => void\n}\n\nexport const ganttKey: InjectionKey<GanttContext> = Symbol('dou-gantt')\n\n/** The surrounding Gantt chart; for layers, overlays and the chart's slots. */\nexport function useGantt() {\n const context = inject(ganttKey, null)\n if (!context) throw new Error('[@dou-ui/gantt] useGantt() must be called inside <DouGantt>')\n return context\n}\n"],"mappings":";;AA+DA,IAAa,WAAuC,OAAO,WAAW;;AAGtE,SAAgB,WAAW;CACzB,MAAM,UAAU,OAAO,UAAU,IAAI;CACrC,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,6DAA6D;CAC3F,OAAO;AACT"}
@@ -0,0 +1,48 @@
1
+ import { MINUTES_PER_DAY, dayStart, formatTime } from "./time.js";
2
+ import { spanOf } from "./model.js";
3
+ import { buildTree } from "./tree.js";
4
+ // src/core/baseline.ts
5
+ /**
6
+ * Saves the current dates of every task as a baseline, summaries included
7
+ * (with the dates their children cover). Tasks without dates are left out.
8
+ */
9
+ function takeBaseline(tasks) {
10
+ const out = [];
11
+ for (const [id, row] of buildTree(tasks).byId) {
12
+ if (!row.span) continue;
13
+ const { start, end, dateOnly } = row.span;
14
+ if (row.milestone) out.push({
15
+ id,
16
+ start: dateOnly ? formatTime(start - MINUTES_PER_DAY / 2, true) : formatTime(start)
17
+ });
18
+ else if (dateOnly) out.push({
19
+ id,
20
+ start: formatTime(start, true),
21
+ end: formatTime(dayStart(end - 1), true)
22
+ });
23
+ else out.push({
24
+ id,
25
+ start: formatTime(start),
26
+ end: formatTime(end)
27
+ });
28
+ }
29
+ return out;
30
+ }
31
+ /** The planned span of each task in a baseline, read as the task's own dates are. */
32
+ function baselineSpans(baseline, milestones) {
33
+ const out = /* @__PURE__ */ new Map();
34
+ for (const item of baseline) {
35
+ const span = spanOf({
36
+ id: item.id,
37
+ title: "",
38
+ type: milestones(item.id) ? "milestone" : void 0,
39
+ start: item.start,
40
+ end: item.end
41
+ });
42
+ if (span) out.set(item.id, span);
43
+ }
44
+ return out;
45
+ }
46
+ export { baselineSpans, takeBaseline };
47
+
48
+ //# sourceMappingURL=baseline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.js","names":[],"sources":["../../src/core/baseline.ts"],"sourcesContent":["import { spanOf, type GanttTask, type TaskSpan } from './model'\nimport { MINUTES_PER_DAY, dayStart, formatTime } from './time'\nimport { buildTree } from './tree'\n\n/** A task's dates as planned, to compare the current ones with. */\nexport interface GanttBaselineItem {\n id: string\n start: string\n end?: string\n}\n\n/**\n * Saves the current dates of every task as a baseline, summaries included\n * (with the dates their children cover). Tasks without dates are left out.\n */\nexport function takeBaseline(tasks: readonly GanttTask[]): GanttBaselineItem[] {\n const out: GanttBaselineItem[] = []\n for (const [id, row] of buildTree(tasks).byId) {\n if (!row.span) continue\n const { start, end, dateOnly } = row.span\n if (row.milestone)\n out.push({\n id,\n start: dateOnly ? formatTime(start - MINUTES_PER_DAY / 2, true) : formatTime(start),\n })\n else if (dateOnly)\n // a summary ending on a milestone ends at noon, on its day\n out.push({ id, start: formatTime(start, true), end: formatTime(dayStart(end - 1), true) })\n else out.push({ id, start: formatTime(start), end: formatTime(end) })\n }\n return out\n}\n\n/** The planned span of each task in a baseline, read as the task's own dates are. */\nexport function baselineSpans(\n baseline: readonly GanttBaselineItem[],\n milestones: (id: string) => boolean,\n): Map<string, TaskSpan> {\n const out = new Map<string, TaskSpan>()\n for (const item of baseline) {\n const span = spanOf({\n id: item.id,\n title: '',\n type: milestones(item.id) ? 'milestone' : undefined,\n start: item.start,\n end: item.end,\n })\n if (span) out.set(item.id, span)\n }\n return out\n}\n"],"mappings":";;;;;;;;AAeA,SAAgB,aAAa,OAAkD;CAC7E,MAAM,MAA2B,CAAC;CAClC,KAAK,MAAM,CAAC,IAAI,QAAQ,UAAU,KAAK,CAAC,CAAC,MAAM;EAC7C,IAAI,CAAC,IAAI,MAAM;EACf,MAAM,EAAE,OAAO,KAAK,aAAa,IAAI;EACrC,IAAI,IAAI,WACN,IAAI,KAAK;GACP;GACA,OAAO,WAAW,WAAW,QAAQ,kBAAkB,GAAG,IAAI,IAAI,WAAW,KAAK;EACpF,CAAC;OACE,IAAI,UAEP,IAAI,KAAK;GAAE;GAAI,OAAO,WAAW,OAAO,IAAI;GAAG,KAAK,WAAW,SAAS,MAAM,CAAC,GAAG,IAAI;EAAE,CAAC;OACtF,IAAI,KAAK;GAAE;GAAI,OAAO,WAAW,KAAK;GAAG,KAAK,WAAW,GAAG;EAAE,CAAC;CACtE;CACA,OAAO;AACT;;AAGA,SAAgB,cACd,UACA,YACuB;CACvB,MAAM,sBAAM,IAAI,IAAsB;CACtC,KAAK,MAAM,QAAQ,UAAU;EAC3B,MAAM,OAAO,OAAO;GAClB,IAAI,KAAK;GACT,OAAO;GACP,MAAM,WAAW,KAAK,EAAE,IAAI,cAAc,KAAA;GAC1C,OAAO,KAAK;GACZ,KAAK,KAAK;EACZ,CAAC;EACD,IAAI,MAAM,IAAI,IAAI,KAAK,IAAI,IAAI;CACjC;CACA,OAAO;AACT"}
@@ -0,0 +1,191 @@
1
+ import { MINUTES_PER_DAY, dayIndex, parseClock, parseTime } from "./time.js";
2
+ // src/core/calendar.ts
3
+ var days = (list) => {
4
+ const out = /* @__PURE__ */ new Set();
5
+ for (const text of list ?? []) {
6
+ const parsed = parseTime(text);
7
+ if (parsed) out.add(dayIndex(parsed.at));
8
+ }
9
+ return out;
10
+ };
11
+ function resolveCalendar(calendar = {}) {
12
+ const weekends = new Set(calendar.weekends ?? [0, 6]);
13
+ const holidays = days(calendar.holidays);
14
+ const workdays = days(calendar.workdays);
15
+ return {
16
+ isWorkday: (day) => workdays.has(day) || !holidays.has(day) && !weekends.has(((day + 4) % 7 + 7) % 7),
17
+ hours: readHours(calendar.hours)
18
+ };
19
+ }
20
+ var DEFAULT_HOURS = [[540, 1080]];
21
+ /** Reads the working stretches, sorted, with overlaps merged; bad ones are dropped. */
22
+ function readHours(hours) {
23
+ if (!hours?.length) return DEFAULT_HOURS;
24
+ const list = typeof hours[0] === "string" ? [hours] : hours;
25
+ const read = [];
26
+ for (const [a, b] of list) {
27
+ const from = parseClock(a);
28
+ const to = parseClock(b);
29
+ if (from != null && to != null && from < to) read.push([from, to]);
30
+ }
31
+ read.sort((x, y) => x[0] - y[0]);
32
+ const out = [];
33
+ for (const range of read) {
34
+ const last = out[out.length - 1];
35
+ if (last && range[0] <= last[1]) last[1] = Math.max(last[1], range[1]);
36
+ else out.push([...range]);
37
+ }
38
+ return out.length ? out : DEFAULT_HOURS;
39
+ }
40
+ /** Working days that start in `[from, to)`. */
41
+ function workdaysBetween(calendar, from, to) {
42
+ let count = 0;
43
+ for (let day = Math.ceil(from / MINUTES_PER_DAY); day * MINUTES_PER_DAY < to; day++) if (calendar.isWorkday(day)) count++;
44
+ return count;
45
+ }
46
+ /** Working minutes in `[from, to)`: the working hours of working days. */
47
+ function workingMinutes(calendar, from, to) {
48
+ let total = 0;
49
+ for (let day = dayIndex(from); day * MINUTES_PER_DAY < to; day++) {
50
+ if (!calendar.isWorkday(day)) continue;
51
+ const base = day * MINUTES_PER_DAY;
52
+ for (const [open, close] of calendar.hours) {
53
+ const start = Math.max(from, base + open);
54
+ const end = Math.min(to, base + close);
55
+ if (end > start) total += end - start;
56
+ }
57
+ }
58
+ return total;
59
+ }
60
+ /**
61
+ * The stretches of `[from, to)` that are not worked: whole days off, and
62
+ * with `hours`, the hours outside work on working days too. Adjacent
63
+ * stretches are merged.
64
+ */
65
+ function offTime(calendar, from, to, hours = false) {
66
+ const out = [];
67
+ const push = (start, end) => {
68
+ start = Math.max(start, from);
69
+ end = Math.min(end, to);
70
+ if (end <= start) return;
71
+ const last = out[out.length - 1];
72
+ if (last && last.end >= start) last.end = Math.max(last.end, end);
73
+ else out.push({
74
+ start,
75
+ end
76
+ });
77
+ };
78
+ for (let day = dayIndex(from); day * MINUTES_PER_DAY < to; day++) {
79
+ const base = day * MINUTES_PER_DAY;
80
+ if (!calendar.isWorkday(day)) push(base, base + MINUTES_PER_DAY);
81
+ else if (hours) {
82
+ let at = 0;
83
+ for (const [open, close] of calendar.hours) {
84
+ push(base + at, base + open);
85
+ at = close;
86
+ }
87
+ push(base + at, base + MINUTES_PER_DAY);
88
+ }
89
+ }
90
+ return out;
91
+ }
92
+ /** How far a search for working time looks, in days. */
93
+ var LOOKAHEAD = 3660;
94
+ /**
95
+ * The midnight `count` working days away from the midnight `at`: forward
96
+ * it ends after the last of them, backward it is the start of the last
97
+ * one. A fraction rounds to whole days.
98
+ */
99
+ function shiftWorkdays(calendar, at, count) {
100
+ let left = Math.round(Math.abs(count));
101
+ const day = dayIndex(at);
102
+ if (!left) return at;
103
+ if (count > 0) {
104
+ for (let d = day; d < day + LOOKAHEAD; d++) if (calendar.isWorkday(d) && --left === 0) return (d + 1) * MINUTES_PER_DAY;
105
+ } else for (let d = day - 1; d > day - LOOKAHEAD; d--) if (calendar.isWorkday(d) && --left === 0) return d * MINUTES_PER_DAY;
106
+ return at + count * MINUTES_PER_DAY;
107
+ }
108
+ /**
109
+ * The moment `count` working minutes away from `at`, forward or backward.
110
+ * It lands inside working hours, or at the end of a stretch going forward.
111
+ */
112
+ function shiftWorkMinutes(calendar, at, count) {
113
+ let left = Math.abs(count);
114
+ if (!left) return at;
115
+ const first = dayIndex(at);
116
+ if (count > 0) for (let day = first; day < first + LOOKAHEAD; day++) {
117
+ if (!calendar.isWorkday(day)) continue;
118
+ const base = day * MINUTES_PER_DAY;
119
+ for (const [open, close] of calendar.hours) {
120
+ const from = Math.max(at, base + open);
121
+ const room = base + close - from;
122
+ if (room <= 0) continue;
123
+ if (left <= room) return from + left;
124
+ left -= room;
125
+ }
126
+ }
127
+ else for (let day = first; day > first - LOOKAHEAD; day--) {
128
+ if (!calendar.isWorkday(day)) continue;
129
+ const base = day * MINUTES_PER_DAY;
130
+ for (let i = calendar.hours.length - 1; i >= 0; i--) {
131
+ const [open, close] = calendar.hours[i];
132
+ const to = Math.min(at, base + close);
133
+ const room = to - (base + open);
134
+ if (room <= 0) continue;
135
+ if (left <= room) return to - left;
136
+ left -= room;
137
+ }
138
+ }
139
+ return at + count;
140
+ }
141
+ /** Working days, or working minutes, in `[from, to)`. */
142
+ var workIn = (calendar, from, to, dateOnly) => dateOnly ? workdaysBetween(calendar, from, to) : workingMinutes(calendar, from, to);
143
+ /**
144
+ * Whether work can start at `at`: the start of a working day for whole
145
+ * days, a moment inside working hours otherwise.
146
+ */
147
+ function isWorkTime(calendar, at, dateOnly) {
148
+ const day = dayIndex(at);
149
+ if (!calendar.isWorkday(day)) return false;
150
+ if (dateOnly) return true;
151
+ const clock = at - day * MINUTES_PER_DAY;
152
+ return calendar.hours.some(([open, close]) => clock >= open && clock < close);
153
+ }
154
+ /**
155
+ * The first moment at or after `at` when work can start: the next working
156
+ * midnight for whole days, the next working minute otherwise.
157
+ */
158
+ function nextWorkTime(calendar, at, dateOnly) {
159
+ const first = dateOnly ? Math.ceil(at / MINUTES_PER_DAY) : dayIndex(at);
160
+ for (let day = first; day < first + LOOKAHEAD; day++) {
161
+ if (!calendar.isWorkday(day)) continue;
162
+ const base = day * MINUTES_PER_DAY;
163
+ if (dateOnly) return base;
164
+ for (const [open, close] of calendar.hours) {
165
+ if (at < base + open) return base + open;
166
+ if (at < base + close) return at;
167
+ }
168
+ }
169
+ return at;
170
+ }
171
+ /** The span's working time: working days for whole days, working minutes otherwise. */
172
+ var workOf = (calendar, span) => workIn(calendar, span.start, span.end, span.dateOnly);
173
+ /**
174
+ * The end of a span moved to start at `start`, keeping its working time:
175
+ * as many working days for a date-only span, as many working minutes
176
+ * otherwise. A span with no working time keeps its length.
177
+ */
178
+ function endKeepingWork(calendar, span, start) {
179
+ const work = workOf(calendar, span);
180
+ if (!work) return start + span.end - span.start;
181
+ return span.dateOnly ? shiftWorkdays(calendar, start, work) : shiftWorkMinutes(calendar, start, work);
182
+ }
183
+ /** The start of a span moved to end at `end`, keeping its working time. */
184
+ function startKeepingWork(calendar, span, end) {
185
+ const work = workOf(calendar, span);
186
+ if (!work) return end - (span.end - span.start);
187
+ return span.dateOnly ? shiftWorkdays(calendar, end, -work) : shiftWorkMinutes(calendar, end, -work);
188
+ }
189
+ export { endKeepingWork, isWorkTime, nextWorkTime, offTime, resolveCalendar, shiftWorkMinutes, shiftWorkdays, startKeepingWork, workIn, workdaysBetween, workingMinutes };
190
+
191
+ //# sourceMappingURL=calendar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.js","names":[],"sources":["../../src/core/calendar.ts"],"sourcesContent":["import { dayIndex, MINUTES_PER_DAY, parseClock, parseTime } from './time'\n\n/** Which days and hours are worked. */\nexport interface GanttCalendar {\n /** Days of the week off, `0` is Sunday. Defaults to `[0, 6]`. */\n weekends?: number[]\n /** Days off besides the weekend, as `YYYY-MM-DD`. */\n holidays?: string[]\n /** Weekend days that are worked anyway, as `YYYY-MM-DD`. They win over `weekends`. */\n workdays?: string[]\n /**\n * The working hours of a working day, as `HH:mm`: one stretch such as\n * `['09:00', '18:00']` (the default), or several to leave out a break,\n * such as `[['09:00', '12:00'], ['13:00', '18:00']]`.\n */\n hours?: [string, string] | [string, string][]\n}\n\n/** A calendar with its strings read, for fast lookups. */\nexport interface WorkCalendar {\n isWorkday: (day: number) => boolean\n /** Working stretches of a day as minutes since midnight, in order and apart. */\n hours: [number, number][]\n}\n\nconst days = (list: string[] | undefined) => {\n const out = new Set<number>()\n for (const text of list ?? []) {\n const parsed = parseTime(text)\n if (parsed) out.add(dayIndex(parsed.at))\n }\n return out\n}\n\nexport function resolveCalendar(calendar: GanttCalendar = {}): WorkCalendar {\n const weekends = new Set(calendar.weekends ?? [0, 6])\n const holidays = days(calendar.holidays)\n const workdays = days(calendar.workdays)\n return {\n // 1970-01-01 was a Thursday\n isWorkday: (day) =>\n workdays.has(day) || (!holidays.has(day) && !weekends.has((((day + 4) % 7) + 7) % 7)),\n hours: readHours(calendar.hours),\n }\n}\n\nconst DEFAULT_HOURS: [number, number][] = [[9 * 60, 18 * 60]]\n\n/** Reads the working stretches, sorted, with overlaps merged; bad ones are dropped. */\nfunction readHours(hours: GanttCalendar['hours']): [number, number][] {\n if (!hours?.length) return DEFAULT_HOURS\n const list = (typeof hours[0] === 'string' ? [hours] : hours) as [string, string][]\n const read: [number, number][] = []\n for (const [a, b] of list) {\n const from = parseClock(a)\n const to = parseClock(b)\n if (from != null && to != null && from < to) read.push([from, to])\n }\n // oxlint-disable-next-line unicorn/no-array-sort -- sorts a fresh array\n read.sort((x, y) => x[0] - y[0])\n const out: [number, number][] = []\n for (const range of read) {\n const last = out[out.length - 1]\n if (last && range[0] <= last[1]) last[1] = Math.max(last[1], range[1])\n else out.push([...range])\n }\n return out.length ? out : DEFAULT_HOURS\n}\n\n/** Working days that start in `[from, to)`. */\nexport function workdaysBetween(calendar: WorkCalendar, from: number, to: number) {\n let count = 0\n for (let day = Math.ceil(from / MINUTES_PER_DAY); day * MINUTES_PER_DAY < to; day++)\n if (calendar.isWorkday(day)) count++\n return count\n}\n\n/** Working minutes in `[from, to)`: the working hours of working days. */\nexport function workingMinutes(calendar: WorkCalendar, from: number, to: number) {\n let total = 0\n for (let day = dayIndex(from); day * MINUTES_PER_DAY < to; day++) {\n if (!calendar.isWorkday(day)) continue\n const base = day * MINUTES_PER_DAY\n for (const [open, close] of calendar.hours) {\n const start = Math.max(from, base + open)\n const end = Math.min(to, base + close)\n if (end > start) total += end - start\n }\n }\n return total\n}\n\nexport interface TimeRange {\n start: number\n end: number\n}\n\n/**\n * The stretches of `[from, to)` that are not worked: whole days off, and\n * with `hours`, the hours outside work on working days too. Adjacent\n * stretches are merged.\n */\nexport function offTime(calendar: WorkCalendar, from: number, to: number, hours = false) {\n const out: TimeRange[] = []\n const push = (start: number, end: number) => {\n start = Math.max(start, from)\n end = Math.min(end, to)\n if (end <= start) return\n const last = out[out.length - 1]\n if (last && last.end >= start) last.end = Math.max(last.end, end)\n else out.push({ start, end })\n }\n for (let day = dayIndex(from); day * MINUTES_PER_DAY < to; day++) {\n const base = day * MINUTES_PER_DAY\n if (!calendar.isWorkday(day)) push(base, base + MINUTES_PER_DAY)\n else if (hours) {\n // the gaps around and between the working stretches\n let at = 0\n for (const [open, close] of calendar.hours) {\n push(base + at, base + open)\n at = close\n }\n push(base + at, base + MINUTES_PER_DAY)\n }\n }\n return out\n}\n\n/** How far a search for working time looks, in days. */\nconst LOOKAHEAD = 3660\n\n/**\n * The midnight `count` working days away from the midnight `at`: forward\n * it ends after the last of them, backward it is the start of the last\n * one. A fraction rounds to whole days.\n */\nexport function shiftWorkdays(calendar: WorkCalendar, at: number, count: number) {\n let left = Math.round(Math.abs(count))\n const day = dayIndex(at)\n if (!left) return at\n if (count > 0) {\n for (let d = day; d < day + LOOKAHEAD; d++)\n if (calendar.isWorkday(d) && --left === 0) return (d + 1) * MINUTES_PER_DAY\n } else {\n for (let d = day - 1; d > day - LOOKAHEAD; d--)\n if (calendar.isWorkday(d) && --left === 0) return d * MINUTES_PER_DAY\n }\n return at + count * MINUTES_PER_DAY\n}\n\n/**\n * The moment `count` working minutes away from `at`, forward or backward.\n * It lands inside working hours, or at the end of a stretch going forward.\n */\nexport function shiftWorkMinutes(calendar: WorkCalendar, at: number, count: number) {\n let left = Math.abs(count)\n if (!left) return at\n const first = dayIndex(at)\n if (count > 0) {\n for (let day = first; day < first + LOOKAHEAD; day++) {\n if (!calendar.isWorkday(day)) continue\n const base = day * MINUTES_PER_DAY\n for (const [open, close] of calendar.hours) {\n const from = Math.max(at, base + open)\n const room = base + close - from\n if (room <= 0) continue\n if (left <= room) return from + left\n left -= room\n }\n }\n } else {\n for (let day = first; day > first - LOOKAHEAD; day--) {\n if (!calendar.isWorkday(day)) continue\n const base = day * MINUTES_PER_DAY\n for (let i = calendar.hours.length - 1; i >= 0; i--) {\n const [open, close] = calendar.hours[i]!\n const to = Math.min(at, base + close)\n const room = to - (base + open)\n if (room <= 0) continue\n if (left <= room) return to - left\n left -= room\n }\n }\n }\n return at + count\n}\n\n/** Working days, or working minutes, in `[from, to)`. */\nexport const workIn = (calendar: WorkCalendar, from: number, to: number, dateOnly: boolean) =>\n dateOnly ? workdaysBetween(calendar, from, to) : workingMinutes(calendar, from, to)\n\n/**\n * Whether work can start at `at`: the start of a working day for whole\n * days, a moment inside working hours otherwise.\n */\nexport function isWorkTime(calendar: WorkCalendar, at: number, dateOnly: boolean) {\n const day = dayIndex(at)\n if (!calendar.isWorkday(day)) return false\n if (dateOnly) return true\n const clock = at - day * MINUTES_PER_DAY\n return calendar.hours.some(([open, close]) => clock >= open && clock < close)\n}\n\n/**\n * The first moment at or after `at` when work can start: the next working\n * midnight for whole days, the next working minute otherwise.\n */\nexport function nextWorkTime(calendar: WorkCalendar, at: number, dateOnly: boolean) {\n const first = dateOnly ? Math.ceil(at / MINUTES_PER_DAY) : dayIndex(at)\n for (let day = first; day < first + LOOKAHEAD; day++) {\n if (!calendar.isWorkday(day)) continue\n const base = day * MINUTES_PER_DAY\n if (dateOnly) return base\n for (const [open, close] of calendar.hours) {\n if (at < base + open) return base + open\n if (at < base + close) return at\n }\n }\n return at\n}\n\n/** The span's working time: working days for whole days, working minutes otherwise. */\nconst workOf = (calendar: WorkCalendar, span: { start: number; end: number; dateOnly: boolean }) =>\n workIn(calendar, span.start, span.end, span.dateOnly)\n\n/**\n * The end of a span moved to start at `start`, keeping its working time:\n * as many working days for a date-only span, as many working minutes\n * otherwise. A span with no working time keeps its length.\n */\nexport function endKeepingWork(\n calendar: WorkCalendar,\n span: { start: number; end: number; dateOnly: boolean },\n start: number,\n) {\n const work = workOf(calendar, span)\n if (!work) return start + span.end - span.start\n return span.dateOnly\n ? shiftWorkdays(calendar, start, work)\n : shiftWorkMinutes(calendar, start, work)\n}\n\n/** The start of a span moved to end at `end`, keeping its working time. */\nexport function startKeepingWork(\n calendar: WorkCalendar,\n span: { start: number; end: number; dateOnly: boolean },\n end: number,\n) {\n const work = workOf(calendar, span)\n if (!work) return end - (span.end - span.start)\n return span.dateOnly\n ? shiftWorkdays(calendar, end, -work)\n : shiftWorkMinutes(calendar, end, -work)\n}\n"],"mappings":"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"}