@temporeum/timeline-vue 0.0.0-stage → 1.0.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.
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +134 -2
- package/dist/index.cjs +266 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +90 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.mjs +256 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +72 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 — 2026-11-07
|
|
4
|
+
|
|
5
|
+
The first release, on 1.0.0 of `@temporeum/timeline-element`. From here on the package follows [semantic versioning](https://horakdani.github.io/Temporeum_timeline_SDK/stability.html).
|
|
6
|
+
|
|
7
|
+
- `<Timeline>` and `useTimelineState` for Vue 3: every `TimelineConfig` field as a declared, typed prop (`TimelineProps<T>`, generic over the items' data; a bare boolean attribute is `true`, a reactive value reaches the instance as the plain object), every instance event as an emit (`TIMELINE_EVENTS` of the element), `ready` with the instance.
|
|
8
|
+
- `registerPlugin` and `hasPlugin` (and the `TimelinePlugin` type) are handed on from `@temporeum/timeline-element`, so an app that installs only the adapter registers the plugins it needs; the element is a `^1.0.0` dependency.
|
|
9
|
+
- The view and the selection are defaults — `defaultDomain` and `defaultSelectedIds` — and stay where the reader moved them on a re-render; the instance's `zoomTo` and `select` move them. Every other field is compared by what it says: an option object written anew on each render is no change, and a drag in progress goes on.
|
|
10
|
+
- Server-rendered by Vue's own renderer (Nuxt included) and hydrated without a second drawing.
|
|
11
|
+
- Options that need a plugin of the element (`gantt`, `resources`, the time tools, `groups`, the item kinds, `strings`, `calendar`, `direction`) need it registered once for the page, with `registerPlugin` from `@temporeum/timeline-element` — in the app's entry, before the first timeline, and where the server renders too. Without it the configuration throws, naming the import.
|
|
12
|
+
- The `#item` slot: each item's content as Vue, teleported into the box over the item — it keeps its state while the item is drawn, and is unmounted when it leaves the view. Needs the element's `/items` plugin, registered once.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Horak and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,135 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @temporeum/timeline-vue
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The timeline as a Vue component, built on the shared instance of
|
|
4
|
+
[`@temporeum/timeline-element`](https://github.com/horakdani/Temporeum_timeline_SDK/tree/main/packages/web-component#readme).
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install @temporeum/timeline-vue @temporeum/timeline-element
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The element is the adapter's own dependency; list it beside the adapter, as
|
|
11
|
+
above, to import its plugins (`@temporeum/timeline-element/items`, `/gantt`,
|
|
12
|
+
`/resources` …): pnpm and Yarn PnP let an app import only what it lists. The
|
|
13
|
+
types an app writes — `Item`, `Lane`, `Group`, `TimeMs` and every event's
|
|
14
|
+
payload (`ItemMovedPayload`, `SelectionChangedPayload` …) — come from
|
|
15
|
+
`@temporeum/timeline-vue` itself.
|
|
16
|
+
|
|
17
|
+
The peer range is `vue ^3.5.0` (`useId` is new in 3.5). The suite runs on
|
|
18
|
+
the newest Vue 3 and, in CI, on 3.5.0, the lowest the range admits.
|
|
19
|
+
|
|
20
|
+
```vue excerpt:template/vue/src/App.vue
|
|
21
|
+
<script setup>
|
|
22
|
+
import { Timeline, useTimelineState } from "@temporeum/timeline-vue";
|
|
23
|
+
import { ref, shallowRef } from "vue";
|
|
24
|
+
|
|
25
|
+
const DAY = 86_400_000;
|
|
26
|
+
const t = Date.UTC(2026, 2, 2);
|
|
27
|
+
const items = [
|
|
28
|
+
{ id: "design", label: "Design", laneId: "work", start: t, end: t + 7 * DAY, data: { owner: "Ana" } },
|
|
29
|
+
{ id: "launch", label: "Launch", laneId: "ship", start: t + 25 * DAY, end: t + 32 * DAY, data: { owner: "Dev" } },
|
|
30
|
+
];
|
|
31
|
+
const lanes = [
|
|
32
|
+
{ id: "work", label: "Work" },
|
|
33
|
+
{ id: "ship", label: "Shipping" },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const timeline = shallowRef(null);
|
|
37
|
+
const selected = useTimelineState(timeline, (state) => state.selectedIds);
|
|
38
|
+
const log = ref("Drag an item.");
|
|
39
|
+
const onMoved = ({ item, to }) => {
|
|
40
|
+
log.value = `${item.label} moved to ${new Date(to.start ?? item.start).toDateString()}`;
|
|
41
|
+
};
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<main style="padding: 16px">
|
|
46
|
+
<Timeline
|
|
47
|
+
:items="items"
|
|
48
|
+
:lanes="lanes"
|
|
49
|
+
:height="320"
|
|
50
|
+
aria-label="Release plan"
|
|
51
|
+
:editing="{ enabled: true, snapMs: DAY }"
|
|
52
|
+
@item-moved="onMoved"
|
|
53
|
+
@ready="timeline = $event"
|
|
54
|
+
>
|
|
55
|
+
<!-- An item's content as Vue: a picture of the item, its label still its name. -->
|
|
56
|
+
<template #item="{ item }">
|
|
57
|
+
<b>{{ item.label }}</b> · {{ item.data.owner }}
|
|
58
|
+
</template>
|
|
59
|
+
</Timeline>
|
|
60
|
+
<p aria-live="polite">{{ log }} {{ selected?.length ?? 0 }} selected.</p>
|
|
61
|
+
</main>
|
|
62
|
+
</template>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The `#item` slot needs the element's `/items` plugin, as `template` does:
|
|
66
|
+
register it once, before the first timeline — without it the timeline
|
|
67
|
+
throws on mount, naming the import.
|
|
68
|
+
|
|
69
|
+
```js excerpt:template/vue/src/main.js
|
|
70
|
+
import { registerPlugin } from "@temporeum/timeline-element";
|
|
71
|
+
import { itemsPlugin } from "@temporeum/timeline-element/items";
|
|
72
|
+
// …
|
|
73
|
+
registerPlugin(itemsPlugin);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The whole project, with Vite, is the [Vue template](https://github.com/horakdani/Temporeum_timeline_SDK/tree/main/packages/docs/templates/vue); a
|
|
77
|
+
[Nuxt one](https://github.com/horakdani/Temporeum_timeline_SDK/tree/main/packages/docs/templates/nuxt) is beside it.
|
|
78
|
+
|
|
79
|
+
Every [`TimelineConfig`](https://horakdani.github.io/Temporeum_timeline_SDK/reference/api.html#configuration) field is a typed
|
|
80
|
+
prop — in camelCase or kebab-case, `:time-zone` is `timeZone`, and a bare
|
|
81
|
+
`lane-headers` is `true`; `class`, `style`, `id` and other attributes go on
|
|
82
|
+
the box —
|
|
83
|
+
and every instance event an emit by its own name (`@item-moved`,
|
|
84
|
+
`@selection-changed`, `@viewport-changed`, …). `@ready` hands over the live
|
|
85
|
+
instance, and `null` on unmount; a template ref's `timeline` is the same
|
|
86
|
+
instance.
|
|
87
|
+
|
|
88
|
+
The view and the selection are where the timeline starts:
|
|
89
|
+
`:default-domain` and `:default-selected-ids`. A re-render does not move
|
|
90
|
+
them back; the instance's `zoomTo` and `select` do. Every other attribute
|
|
91
|
+
is compared by what it says, so an `:editing="{ enabled: true }"` object
|
|
92
|
+
written anew on each render is no change.
|
|
93
|
+
|
|
94
|
+
## It does not re-render to draw
|
|
95
|
+
|
|
96
|
+
The component creates the instance once, on mount, and hands it the
|
|
97
|
+
attributes that changed — the same `items` array is not new data. A pan, a
|
|
98
|
+
pinch, a drag are drawn by the instance, imperatively; Vue does not hear of
|
|
99
|
+
them. What a component renders from is the instance's store:
|
|
100
|
+
`useTimelineState(timeline, selector, isEqual?)` gives a shallow ref that
|
|
101
|
+
changes when the selected part does, and at most once per drawn frame,
|
|
102
|
+
however many pointer events the frame held. `timeline` may be a ref, a
|
|
103
|
+
getter (`() => view.value?.timeline`) or the instance.
|
|
104
|
+
|
|
105
|
+
## Server rendering
|
|
106
|
+
|
|
107
|
+
On the server — Nuxt, or `renderToString` of your own — the component prints
|
|
108
|
+
the drawing the browser would draw, at `ssr-width` (960 px by default), and
|
|
109
|
+
on hydration the instance takes those nodes over instead of drawing again.
|
|
110
|
+
Nothing else is needed: no client plugin, no compiler option.
|
|
111
|
+
|
|
112
|
+
```vue excerpt:template/nuxt/app/app.vue
|
|
113
|
+
<template>
|
|
114
|
+
<main>
|
|
115
|
+
<Timeline :items="items" :lanes="lanes" :height="320" aria-label="Release plan" :ssr-width="960" />
|
|
116
|
+
</main>
|
|
117
|
+
</template>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The drawing goes into the server's markup only. Vue 3.5 writes a dynamic
|
|
121
|
+
`innerHTML` again while hydrating, and an `innerHTML` written by script is a
|
|
122
|
+
second drawing, not the parser's first; on the client the component's node
|
|
123
|
+
has no children for Vue to compare, so Vue leaves alone what the parser
|
|
124
|
+
built.
|
|
125
|
+
|
|
126
|
+
## Documentation
|
|
127
|
+
|
|
128
|
+
Guides, recipes and the API reference with live examples are on the
|
|
129
|
+
[docs site](https://horakdani.github.io/Temporeum_timeline_SDK/) — the Vue page is [Frameworks](https://horakdani.github.io/Temporeum_timeline_SDK/frameworks.html). Every
|
|
130
|
+
code block in this README is checked against a running template in CI
|
|
131
|
+
(`npm run test:examples -w @temporeum/timeline-docs`).
|
|
132
|
+
|
|
133
|
+
## Licence
|
|
134
|
+
|
|
135
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/*! @temporeum/timeline-vue | MIT */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var timelineElement = require('@temporeum/timeline-element');
|
|
5
|
+
var vue = require('vue');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `@temporeum/timeline-vue` — the timeline as a Vue component.
|
|
9
|
+
*
|
|
10
|
+
* The component is a shell around the shared instance (`createTimeline`,
|
|
11
|
+
* from `@temporeum/timeline-element`), as the React one is: it creates the
|
|
12
|
+
* instance once, on mount, hands it every attribute that changed, and lets
|
|
13
|
+
* go of it on unmount. It does not re-render to draw. A pan of a hundred
|
|
14
|
+
* pointer events is drawn by the instance, imperatively, and Vue does not
|
|
15
|
+
* hear of it — its reactivity is the wrong tool for sixty frames a second of
|
|
16
|
+
* SVG attributes.
|
|
17
|
+
*
|
|
18
|
+
* What a component *does* render from is the instance's store.
|
|
19
|
+
* `useTimelineState` subscribes to it with a selector and gives a shallow
|
|
20
|
+
* ref: one that shows the selection changes when the selection changes and
|
|
21
|
+
* at no other time; one that shows the visible range changes once per drawn
|
|
22
|
+
* frame at most, however many events the frame held.
|
|
23
|
+
*
|
|
24
|
+
* **Server rendering.** On the server the component prints the same drawing
|
|
25
|
+
* the browser would draw (the core is DOM-free), so the page shows a
|
|
26
|
+
* timeline before the bundle loads; on hydration the instance takes those
|
|
27
|
+
* nodes over. The drawing goes into the server's markup only: Vue 3.5
|
|
28
|
+
* writes a dynamic `innerHTML` again while hydrating, and an `innerHTML`
|
|
29
|
+
* written by script is not the parser's — the instance would draw a second
|
|
30
|
+
* timeline. On the client the node has no children for Vue to compare, so
|
|
31
|
+
* Vue leaves alone what the parser built.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* A prop that may be written as a bare attribute (`<Timeline lane-headers />`)
|
|
35
|
+
* is a boolean first: Vue reads the empty string as `true` for it. The rest
|
|
36
|
+
* take any value, and an absent one is `undefined` — not in the
|
|
37
|
+
* configuration, so the instance's default holds.
|
|
38
|
+
*/
|
|
39
|
+
const ANY = { type: null, default: undefined };
|
|
40
|
+
const FLAG = { type: Boolean, default: undefined };
|
|
41
|
+
const PROPS = {
|
|
42
|
+
items: ANY,
|
|
43
|
+
lanes: ANY,
|
|
44
|
+
groups: ANY,
|
|
45
|
+
template: ANY,
|
|
46
|
+
templateOptions: ANY,
|
|
47
|
+
slots: { type: [Boolean, Function], default: undefined },
|
|
48
|
+
gantt: ANY,
|
|
49
|
+
resources: ANY,
|
|
50
|
+
resourceOptions: ANY,
|
|
51
|
+
defaultDomain: ANY,
|
|
52
|
+
limits: ANY,
|
|
53
|
+
orientation: ANY,
|
|
54
|
+
breakpoint: ANY,
|
|
55
|
+
density: ANY,
|
|
56
|
+
metrics: ANY,
|
|
57
|
+
packMode: ANY,
|
|
58
|
+
theme: ANY,
|
|
59
|
+
locale: ANY,
|
|
60
|
+
timeZone: ANY,
|
|
61
|
+
formatTick: ANY,
|
|
62
|
+
axisContext: FLAG,
|
|
63
|
+
touchAction: ANY,
|
|
64
|
+
maxLabels: ANY,
|
|
65
|
+
labelOverflow: ANY,
|
|
66
|
+
ariaLabel: ANY,
|
|
67
|
+
decorations: ANY,
|
|
68
|
+
cluster: { type: [Boolean, Object], default: undefined },
|
|
69
|
+
hooks: ANY,
|
|
70
|
+
height: ANY,
|
|
71
|
+
interactions: ANY,
|
|
72
|
+
editing: ANY,
|
|
73
|
+
defaultSelectedIds: ANY,
|
|
74
|
+
instanceId: ANY,
|
|
75
|
+
nonce: ANY,
|
|
76
|
+
measureText: ANY,
|
|
77
|
+
sceneSpace: { type: [Boolean, String], default: undefined },
|
|
78
|
+
hiddenDates: ANY,
|
|
79
|
+
timeBars: ANY,
|
|
80
|
+
timeBarOptions: ANY,
|
|
81
|
+
rolling: { type: [Boolean, Object], default: undefined },
|
|
82
|
+
zoomPresets: { type: [Boolean, Array], default: undefined },
|
|
83
|
+
minimap: { type: [Boolean, Object], default: undefined },
|
|
84
|
+
rangeSelect: { type: [Boolean, Object], default: undefined },
|
|
85
|
+
laneHeaders: FLAG,
|
|
86
|
+
tooltip: { type: [Boolean, Function], default: undefined },
|
|
87
|
+
direction: ANY,
|
|
88
|
+
strings: ANY,
|
|
89
|
+
calendar: ANY,
|
|
90
|
+
ssrWidth: { type: Number, default: undefined },
|
|
91
|
+
};
|
|
92
|
+
/** Where the view and the selection start, and the server's width: said once, not configuration that changes. */
|
|
93
|
+
const OWN = new Set(["ssrWidth", "defaultDomain", "defaultSelectedIds"]);
|
|
94
|
+
/** `useId` gives `v-0` or `v:0`; a stylesheet scope wants a plain name. */
|
|
95
|
+
const scopeOf = (id) => `tl${id.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
96
|
+
/**
|
|
97
|
+
* The configuration part of the props: the ones given, as plain values. A
|
|
98
|
+
* reactive array or object the host passes is Vue's proxy of it; the
|
|
99
|
+
* instance holds the object itself, so its identity is the host's and the
|
|
100
|
+
* frames do not go through the proxy.
|
|
101
|
+
*/
|
|
102
|
+
function configOf(props, scope) {
|
|
103
|
+
const config = {};
|
|
104
|
+
for (const key of Object.keys(PROPS)) {
|
|
105
|
+
const value = props[key];
|
|
106
|
+
if (!OWN.has(key) && value !== undefined)
|
|
107
|
+
config[key] = vue.toRaw(value);
|
|
108
|
+
}
|
|
109
|
+
// Light DOM: two timelines on one page must not share one stylesheet.
|
|
110
|
+
if (config.instanceId === undefined)
|
|
111
|
+
config.instanceId = scope;
|
|
112
|
+
return config;
|
|
113
|
+
}
|
|
114
|
+
/** The keys whose values are not the same object: an unchanged `items` array must not look like new data. */
|
|
115
|
+
function changedOf(before, next) {
|
|
116
|
+
const changed = {};
|
|
117
|
+
let any = false;
|
|
118
|
+
for (const key of new Set([...Object.keys(before), ...Object.keys(next)])) {
|
|
119
|
+
if (key !== "__proto__" && before[key] !== next[key]) {
|
|
120
|
+
changed[key] = next[key];
|
|
121
|
+
any = true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return any ? changed : undefined;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The timeline. Every {@link TimelineConfig} field is a prop (`:items`,
|
|
128
|
+
* `:time-zone` or `:timeZone`, a bare `lane-headers`), every instance event
|
|
129
|
+
* an emit (`@item-moved`, `@selection-changed`, …), and `ready` hands over
|
|
130
|
+
* the live instance; a template ref's `timeline` is the same instance.
|
|
131
|
+
* `class`, `style`, `id` and the rest of the attributes go on the box.
|
|
132
|
+
*
|
|
133
|
+
* The `#item` slot (`<template #item="{ item }">`) is each item's content
|
|
134
|
+
* as Vue, teleported into the box over the item (`slots`): only the items
|
|
135
|
+
* drawn have one. The content is a picture — the pointer and the keyboard
|
|
136
|
+
* act on the item, and its `label` stays its accessible name. Needs the
|
|
137
|
+
* `/items` plugin, as `template` does.
|
|
138
|
+
*/
|
|
139
|
+
const Timeline = vue.defineComponent((props, { emit, expose, slots }) => {
|
|
140
|
+
// Vue 3.5.0 types useId() as possibly undefined (outside a setup); here it is in one.
|
|
141
|
+
const scope = scopeOf(vue.useId() ?? "");
|
|
142
|
+
const container = vue.ref(null);
|
|
143
|
+
const timeline = vue.shallowRef(null);
|
|
144
|
+
const drawn = vue.shallowRef(new Map());
|
|
145
|
+
// With an `#item` slot every item has a box, unless `slots` picks.
|
|
146
|
+
const configNow = () => {
|
|
147
|
+
const config = configOf(props, scope);
|
|
148
|
+
if (slots.item !== undefined && config.slots === undefined)
|
|
149
|
+
config.slots = true;
|
|
150
|
+
return config;
|
|
151
|
+
};
|
|
152
|
+
let applied = configNow();
|
|
153
|
+
const initial = {
|
|
154
|
+
...applied,
|
|
155
|
+
...(props.defaultDomain === undefined ? {} : { domain: vue.toRaw(props.defaultDomain) }),
|
|
156
|
+
...(props.defaultSelectedIds === undefined ? {} : { selectedIds: vue.toRaw(props.defaultSelectedIds) }),
|
|
157
|
+
};
|
|
158
|
+
// The server's drawing, computed once — on the server only: Vue's
|
|
159
|
+
// server renderer is what provides an SSR context.
|
|
160
|
+
const html = vue.inject(vue.ssrContextKey, null) !== null
|
|
161
|
+
? (() => {
|
|
162
|
+
const frame = timelineElement.renderTimelineSvg(initial, props.ssrWidth === undefined ? {} : { width: props.ssrWidth });
|
|
163
|
+
return frame.svg + frame.groups;
|
|
164
|
+
})()
|
|
165
|
+
: undefined;
|
|
166
|
+
let stops = [];
|
|
167
|
+
vue.onMounted(() => {
|
|
168
|
+
const instance = timelineElement.createTimeline(container.value, { ...initial, ...applied }, { hydrate: true });
|
|
169
|
+
stops = timelineElement.TIMELINE_EVENTS.map((type) => instance.on(type, (payload) => forward(emit, type, payload)));
|
|
170
|
+
// Vue renders the teleports in its own flush, a microtask after the
|
|
171
|
+
// frame that drew the boxes: before the browser paints it.
|
|
172
|
+
stops.push(instance.on("slotsChanged", ({ slots: next }) => (drawn.value = next)));
|
|
173
|
+
drawn.value = instance.slots;
|
|
174
|
+
timeline.value = instance;
|
|
175
|
+
emit("ready", instance);
|
|
176
|
+
});
|
|
177
|
+
// Hand the instance what changed, and only that.
|
|
178
|
+
vue.onUpdated(() => {
|
|
179
|
+
const next = configNow();
|
|
180
|
+
const changed = changedOf(applied, next);
|
|
181
|
+
applied = next;
|
|
182
|
+
if (changed !== undefined)
|
|
183
|
+
timeline.value?.update(changed);
|
|
184
|
+
});
|
|
185
|
+
vue.onBeforeUnmount(() => {
|
|
186
|
+
for (const stop of stops)
|
|
187
|
+
stop();
|
|
188
|
+
stops = [];
|
|
189
|
+
timeline.value?.destroy();
|
|
190
|
+
timeline.value = null;
|
|
191
|
+
emit("ready", null);
|
|
192
|
+
});
|
|
193
|
+
expose({ timeline });
|
|
194
|
+
return () => {
|
|
195
|
+
const height = props.height ?? timelineElement.DEFAULT_HEIGHT;
|
|
196
|
+
const item = slots.item;
|
|
197
|
+
const teleports = item === undefined ? [] : [...drawn.value].map(([id, slot]) => vue.h(vue.Teleport, { key: id, to: slot.element }, item({ item: slot.item })));
|
|
198
|
+
// The teleports are the box's children, as anchors: one root, so the
|
|
199
|
+
// server's markup stays the box alone. Without a teleport the box has
|
|
200
|
+
// no children for Vue — on the server it has the drawing, and while
|
|
201
|
+
// hydrating Vue must not compare the instance's nodes with any. The
|
|
202
|
+
// host's class, style and attributes fall through onto the box.
|
|
203
|
+
return vue.h("div", {
|
|
204
|
+
ref: container,
|
|
205
|
+
style: { position: "relative", overflow: "hidden", height: `${String(height)}px` },
|
|
206
|
+
"data-tl-vue": "",
|
|
207
|
+
...(html === undefined ? {} : { innerHTML: html }),
|
|
208
|
+
}, html === undefined && teleports.length > 0 ? teleports : undefined);
|
|
209
|
+
};
|
|
210
|
+
}, {
|
|
211
|
+
name: "TemporeumTimeline",
|
|
212
|
+
// The runtime declaration; the types are the setup function's, generic over the items' data.
|
|
213
|
+
props: PROPS,
|
|
214
|
+
emits: [...timelineElement.TIMELINE_EVENTS, "ready"],
|
|
215
|
+
slots: Object,
|
|
216
|
+
});
|
|
217
|
+
/** One instance event, emitted under its own name: the payload is that event's. */
|
|
218
|
+
function forward(emit, type, payload) {
|
|
219
|
+
emit(type, payload);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Subscribe to the part of the instance's state a component renders.
|
|
223
|
+
*
|
|
224
|
+
* The ref changes when `selector`'s result changes (by `isEqual`,
|
|
225
|
+
* `Object.is` by default) — and at most once per drawn frame, because the
|
|
226
|
+
* store notifies at most once per frame. `undefined` until there is an
|
|
227
|
+
* instance; `timeline` may be a ref, a getter (`() => view.value?.timeline`)
|
|
228
|
+
* or the instance itself.
|
|
229
|
+
*/
|
|
230
|
+
function useTimelineState(timeline, selector, isEqual = Object.is) {
|
|
231
|
+
const state = vue.shallowRef(undefined);
|
|
232
|
+
vue.watch(() => vue.toValue(timeline), (instance, _, onCleanup) => {
|
|
233
|
+
if (instance === null || instance === undefined) {
|
|
234
|
+
state.value = undefined;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
let seen;
|
|
238
|
+
const read = () => {
|
|
239
|
+
const snapshot = instance.getSnapshot();
|
|
240
|
+
if (seen !== undefined && seen.snapshot === snapshot)
|
|
241
|
+
return;
|
|
242
|
+
const selected = selector(snapshot);
|
|
243
|
+
if (seen !== undefined && isEqual(seen.selected, selected)) {
|
|
244
|
+
seen = { snapshot, selected: seen.selected };
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
seen = { snapshot, selected };
|
|
248
|
+
state.value = selected;
|
|
249
|
+
};
|
|
250
|
+
read();
|
|
251
|
+
onCleanup(instance.subscribe(read));
|
|
252
|
+
}, { immediate: true });
|
|
253
|
+
return state;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
Object.defineProperty(exports, "hasPlugin", {
|
|
257
|
+
enumerable: true,
|
|
258
|
+
get: function () { return timelineElement.hasPlugin; }
|
|
259
|
+
});
|
|
260
|
+
Object.defineProperty(exports, "registerPlugin", {
|
|
261
|
+
enumerable: true,
|
|
262
|
+
get: function () { return timelineElement.registerPlugin; }
|
|
263
|
+
});
|
|
264
|
+
exports.Timeline = Timeline;
|
|
265
|
+
exports.useTimelineState = useTimelineState;
|
|
266
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["/**\n * `@temporeum/timeline-vue` — the timeline as a Vue component.\n *\n * The component is a shell around the shared instance (`createTimeline`,\n * from `@temporeum/timeline-element`), as the React one is: it creates the\n * instance once, on mount, hands it every attribute that changed, and lets\n * go of it on unmount. It does not re-render to draw. A pan of a hundred\n * pointer events is drawn by the instance, imperatively, and Vue does not\n * hear of it — its reactivity is the wrong tool for sixty frames a second of\n * SVG attributes.\n *\n * What a component *does* render from is the instance's store.\n * `useTimelineState` subscribes to it with a selector and gives a shallow\n * ref: one that shows the selection changes when the selection changes and\n * at no other time; one that shows the visible range changes once per drawn\n * frame at most, however many events the frame held.\n *\n * **Server rendering.** On the server the component prints the same drawing\n * the browser would draw (the core is DOM-free), so the page shows a\n * timeline before the bundle loads; on hydration the instance takes those\n * nodes over. The drawing goes into the server's markup only: Vue 3.5\n * writes a dynamic `innerHTML` again while hydrating, and an `innerHTML`\n * written by script is not the parser's — the instance would draw a second\n * timeline. On the client the node has no children for Vue to compare, so\n * Vue leaves alone what the parser built.\n */\n\nimport { createTimeline, DEFAULT_HEIGHT, renderTimelineSvg, TIMELINE_EVENTS } from \"@temporeum/timeline-element\";\nimport type {\n ItemSlot,\n Timeline as TimelineInstance,\n TimelineComponentConfig,\n TimelineConfig,\n TimelineInstanceEventMap,\n TimelineInstanceEventName,\n TimelineSnapshot,\n TimelineUpdate,\n} from \"@temporeum/timeline-element\";\nimport { defineComponent, h, inject, onBeforeUnmount, onMounted, onUpdated, ref, shallowRef, ssrContextKey, Teleport, toRaw, toValue, useId, watch } from \"vue\";\nimport type {\n AllowedComponentProps,\n ComponentCustomProps,\n ComponentOptionsMixin,\n CreateComponentPublicInstanceWithMixins,\n MaybeRefOrGetter,\n PublicProps,\n SetupContext,\n ShallowRef,\n SlotsType,\n VNodeProps,\n} from \"vue\";\n\nexport type { TimelineComponentConfig, TimelineConfig, TimelineSnapshot } from \"@temporeum/timeline-element\";\n/** The element's plugin registry, from the adapter too: an app that installs only the adapter still registers the plugins its configuration needs. */\n// The data and the event payloads, handed on: one install types an app.\nexport type {\n ClusterOpenedPayload,\n Command,\n CommandPayload,\n DensityChangedPayload,\n Domain,\n Group,\n GroupMovedPayload,\n GroupToggledPayload,\n HoverPayload,\n InputSkippedPayload,\n Item,\n ItemEventPayload,\n ItemMovedPayload,\n ItemPatch,\n ItemResizedPayload,\n ItemsAddedPayload,\n ItemsChangedPayload,\n ItemsRemovedPayload,\n JumpRequestPayload,\n Lane,\n LayoutChangedPayload,\n OrientationChangedPayload,\n SelectionChangedPayload,\n SlotsChangedPayload,\n TimeBar,\n TimeBarMovedPayload,\n TimelineInstanceEventMap,\n TimelineInstanceEventName,\n TimeMs,\n TimeRange,\n ViewportChangedPayload,\n} from \"@temporeum/timeline-element\";\nexport { hasPlugin, registerPlugin } from \"@temporeum/timeline-element\";\nexport type { PluginName, TimelinePlugin } from \"@temporeum/timeline-element\";\nexport type { TimelineInstance };\n\n/** What the component emits: every instance event, by its own name, and `ready` with the instance (`null` on unmount). */\nexport type TimelineEmits<T = unknown> = { readonly [K in TimelineInstanceEventName]: (payload: TimelineInstanceEventMap<T>[K]) => void } & {\n readonly ready: (timeline: TimelineInstance<T> | null) => void;\n};\n\n/** The component's props: every {@link TimelineComponentConfig} field, and the width the server draws at. */\nexport type TimelineProps<T = unknown> = TimelineComponentConfig<T> & {\n /** Width the server draws at; the client redraws at the real one. */\n readonly ssrWidth?: number | undefined;\n};\n\n/** The `#item` slot: each drawn item's content. */\nexport type TimelineSlots<T = unknown> = SlotsType<{ readonly item: { readonly item: Item<T> } }>;\n\n/** A handler prop for every event — `onItemMoved`, `onReady`, … — its payload typed by the items' data. */\nexport type TimelineListeners<T = unknown> = { readonly [K in keyof TimelineEmits<T> & string as `on${Capitalize<K>}`]?: TimelineEmits<T>[K] };\n\n/**\n * The component's type, generic over the items' data: every prop, every\n * handler and the `#item` slot typed by it, and a template ref's `timeline`.\n * Written out because Vue types the runtime `emits` list — the names — as\n * handlers of `(...args: any[]) => any`, and the options' slots as `unknown`.\n */\nexport type TimelineComponent = new <T = unknown>(\n props: TimelineProps<T> & TimelineListeners<T> & VNodeProps & AllowedComponentProps & ComponentCustomProps,\n) => CreateComponentPublicInstanceWithMixins<\n TimelineProps<T> & TimelineListeners<T>,\n { readonly timeline: TimelineInstance<T> | null },\n Record<never, never>,\n Record<never, never>,\n Record<never, never>,\n ComponentOptionsMixin,\n ComponentOptionsMixin,\n TimelineEmits<T>,\n PublicProps,\n Record<never, never>,\n false,\n Record<never, never>,\n TimelineSlots<T>\n>;\n\n/** An item, as the element's types name it (the adapter depends on the element alone). */\ntype Item<T> = ItemSlot<T>[\"item\"];\n\n/**\n * A prop that may be written as a bare attribute (`<Timeline lane-headers />`)\n * is a boolean first: Vue reads the empty string as `true` for it. The rest\n * take any value, and an absent one is `undefined` — not in the\n * configuration, so the instance's default holds.\n */\nconst ANY = { type: null, default: undefined } as const;\nconst FLAG = { type: Boolean, default: undefined } as const;\nconst PROPS: { readonly [K in keyof TimelineProps]-?: { readonly type: unknown; readonly default: undefined } } = {\n items: ANY,\n lanes: ANY,\n groups: ANY,\n template: ANY,\n templateOptions: ANY,\n slots: { type: [Boolean, Function], default: undefined },\n gantt: ANY,\n resources: ANY,\n resourceOptions: ANY,\n defaultDomain: ANY,\n limits: ANY,\n orientation: ANY,\n breakpoint: ANY,\n density: ANY,\n metrics: ANY,\n packMode: ANY,\n theme: ANY,\n locale: ANY,\n timeZone: ANY,\n formatTick: ANY,\n axisContext: FLAG,\n touchAction: ANY,\n maxLabels: ANY,\n labelOverflow: ANY,\n ariaLabel: ANY,\n decorations: ANY,\n cluster: { type: [Boolean, Object], default: undefined },\n hooks: ANY,\n height: ANY,\n interactions: ANY,\n editing: ANY,\n defaultSelectedIds: ANY,\n instanceId: ANY,\n nonce: ANY,\n measureText: ANY,\n sceneSpace: { type: [Boolean, String], default: undefined },\n hiddenDates: ANY,\n timeBars: ANY,\n timeBarOptions: ANY,\n rolling: { type: [Boolean, Object], default: undefined },\n zoomPresets: { type: [Boolean, Array], default: undefined },\n minimap: { type: [Boolean, Object], default: undefined },\n rangeSelect: { type: [Boolean, Object], default: undefined },\n laneHeaders: FLAG,\n tooltip: { type: [Boolean, Function], default: undefined },\n direction: ANY,\n strings: ANY,\n calendar: ANY,\n ssrWidth: { type: Number, default: undefined },\n};\n\n/** Where the view and the selection start, and the server's width: said once, not configuration that changes. */\nconst OWN = new Set([\"ssrWidth\", \"defaultDomain\", \"defaultSelectedIds\"]);\n\n/** `useId` gives `v-0` or `v:0`; a stylesheet scope wants a plain name. */\nconst scopeOf = (id: string): string => `tl${id.replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n\n/**\n * The configuration part of the props: the ones given, as plain values. A\n * reactive array or object the host passes is Vue's proxy of it; the\n * instance holds the object itself, so its identity is the host's and the\n * frames do not go through the proxy.\n */\nfunction configOf(props: Record<string, unknown>, scope: string): Record<string, unknown> {\n const config: Record<string, unknown> = {};\n for (const key of Object.keys(PROPS)) {\n const value = props[key];\n if (!OWN.has(key) && value !== undefined) config[key] = toRaw(value);\n }\n // Light DOM: two timelines on one page must not share one stylesheet.\n if (config.instanceId === undefined) config.instanceId = scope;\n return config;\n}\n\n/** The keys whose values are not the same object: an unchanged `items` array must not look like new data. */\nfunction changedOf(before: Record<string, unknown>, next: Record<string, unknown>): Record<string, unknown> | undefined {\n const changed: Record<string, unknown> = {};\n let any = false;\n for (const key of new Set([...Object.keys(before), ...Object.keys(next)])) {\n if (key !== \"__proto__\" && before[key] !== next[key]) {\n changed[key] = next[key];\n any = true;\n }\n }\n return any ? changed : undefined;\n}\n\n/**\n * The timeline. Every {@link TimelineConfig} field is a prop (`:items`,\n * `:time-zone` or `:timeZone`, a bare `lane-headers`), every instance event\n * an emit (`@item-moved`, `@selection-changed`, …), and `ready` hands over\n * the live instance; a template ref's `timeline` is the same instance.\n * `class`, `style`, `id` and the rest of the attributes go on the box.\n *\n * The `#item` slot (`<template #item=\"{ item }\">`) is each item's content\n * as Vue, teleported into the box over the item (`slots`): only the items\n * drawn have one. The content is a picture — the pointer and the keyboard\n * act on the item, and its `label` stays its accessible name. Needs the\n * `/items` plugin, as `template` does.\n */\nexport const Timeline: TimelineComponent = defineComponent(\n <T = unknown>(props: TimelineProps<T>, { emit, expose, slots }: SetupContext<TimelineEmits<T>, TimelineSlots>) => {\n // Vue 3.5.0 types useId() as possibly undefined (outside a setup); here it is in one.\n const scope = scopeOf(useId() ?? \"\");\n const container = ref<HTMLDivElement | null>(null);\n const timeline = shallowRef<TimelineInstance<T> | null>(null);\n const drawn = shallowRef<ReadonlyMap<string, ItemSlot<T>>>(new Map());\n // With an `#item` slot every item has a box, unless `slots` picks.\n const configNow = (): Record<string, unknown> => {\n const config = configOf(props as Record<string, unknown>, scope);\n if (slots.item !== undefined && config.slots === undefined) config.slots = true;\n return config;\n };\n let applied = configNow();\n const initial: Record<string, unknown> = {\n ...applied,\n ...(props.defaultDomain === undefined ? {} : { domain: toRaw(props.defaultDomain) }),\n ...(props.defaultSelectedIds === undefined ? {} : { selectedIds: toRaw(props.defaultSelectedIds) }),\n };\n // The server's drawing, computed once — on the server only: Vue's\n // server renderer is what provides an SSR context.\n const html =\n inject(ssrContextKey, null) !== null\n ? (() => {\n const frame = renderTimelineSvg(initial as TimelineConfig<T>, props.ssrWidth === undefined ? {} : { width: props.ssrWidth });\n return frame.svg + frame.groups;\n })()\n : undefined;\n let stops: (() => void)[] = [];\n\n onMounted(() => {\n const instance = createTimeline<T>(container.value as HTMLDivElement, { ...initial, ...applied } as TimelineConfig<T>, { hydrate: true });\n stops = TIMELINE_EVENTS.map((type) => instance.on(type, (payload) => forward(emit, type, payload)));\n // Vue renders the teleports in its own flush, a microtask after the\n // frame that drew the boxes: before the browser paints it.\n stops.push(instance.on(\"slotsChanged\", ({ slots: next }) => (drawn.value = next)));\n drawn.value = instance.slots;\n timeline.value = instance;\n emit(\"ready\", instance);\n });\n\n // Hand the instance what changed, and only that.\n onUpdated(() => {\n const next = configNow();\n const changed = changedOf(applied, next);\n applied = next;\n if (changed !== undefined) timeline.value?.update(changed as TimelineUpdate<T>);\n });\n\n onBeforeUnmount(() => {\n for (const stop of stops) stop();\n stops = [];\n timeline.value?.destroy();\n timeline.value = null;\n emit(\"ready\", null);\n });\n\n expose({ timeline });\n\n return () => {\n const height = props.height ?? DEFAULT_HEIGHT;\n const item = slots.item;\n const teleports = item === undefined ? [] : [...drawn.value].map(([id, slot]) => h(Teleport, { key: id, to: slot.element }, item({ item: slot.item })));\n // The teleports are the box's children, as anchors: one root, so the\n // server's markup stays the box alone. Without a teleport the box has\n // no children for Vue — on the server it has the drawing, and while\n // hydrating Vue must not compare the instance's nodes with any. The\n // host's class, style and attributes fall through onto the box.\n return h(\n \"div\",\n {\n ref: container,\n style: { position: \"relative\", overflow: \"hidden\", height: `${String(height)}px` },\n \"data-tl-vue\": \"\",\n ...(html === undefined ? {} : { innerHTML: html }),\n },\n html === undefined && teleports.length > 0 ? teleports : undefined,\n );\n };\n },\n {\n name: \"TemporeumTimeline\",\n // The runtime declaration; the types are the setup function's, generic over the items' data.\n props: PROPS as never,\n emits: [...TIMELINE_EVENTS, \"ready\"],\n slots: Object as TimelineSlots,\n },\n) as unknown as TimelineComponent;\n\n/** One instance event, emitted under its own name: the payload is that event's. */\nfunction forward<T, K extends TimelineInstanceEventName>(emit: SetupContext<TimelineEmits<T>>[\"emit\"], type: K, payload: TimelineInstanceEventMap<T>[K]): void {\n (emit as (type: K, payload: TimelineInstanceEventMap<T>[K]) => void)(type, payload);\n}\n\n/**\n * Subscribe to the part of the instance's state a component renders.\n *\n * The ref changes when `selector`'s result changes (by `isEqual`,\n * `Object.is` by default) — and at most once per drawn frame, because the\n * store notifies at most once per frame. `undefined` until there is an\n * instance; `timeline` may be a ref, a getter (`() => view.value?.timeline`)\n * or the instance itself.\n */\nexport function useTimelineState<T, S>(\n timeline: MaybeRefOrGetter<TimelineInstance<T> | null | undefined>,\n selector: (snapshot: TimelineSnapshot) => S,\n isEqual: (a: S, b: S) => boolean = Object.is,\n): Readonly<ShallowRef<S | undefined>> {\n const state = shallowRef<S | undefined>(undefined);\n watch(\n () => toValue(timeline),\n (instance, _, onCleanup) => {\n if (instance === null || instance === undefined) {\n state.value = undefined;\n return;\n }\n let seen: { snapshot: TimelineSnapshot; selected: S } | undefined;\n const read = (): void => {\n const snapshot = instance.getSnapshot();\n if (seen !== undefined && seen.snapshot === snapshot) return;\n const selected = selector(snapshot);\n if (seen !== undefined && isEqual(seen.selected, selected)) {\n seen = { snapshot, selected: seen.selected };\n return;\n }\n seen = { snapshot, selected };\n state.value = selected;\n };\n read();\n onCleanup(instance.subscribe(read));\n },\n { immediate: true },\n );\n return state;\n}\n"],"names":["toRaw","defineComponent","useId","ref","shallowRef","inject","ssrContextKey","renderTimelineSvg","onMounted","createTimeline","TIMELINE_EVENTS","onUpdated","onBeforeUnmount","DEFAULT_HEIGHT","h","Teleport","watch","toValue"],"mappings":"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"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@temporeum/timeline-vue` — the timeline as a Vue component.
|
|
3
|
+
*
|
|
4
|
+
* The component is a shell around the shared instance (`createTimeline`,
|
|
5
|
+
* from `@temporeum/timeline-element`), as the React one is: it creates the
|
|
6
|
+
* instance once, on mount, hands it every attribute that changed, and lets
|
|
7
|
+
* go of it on unmount. It does not re-render to draw. A pan of a hundred
|
|
8
|
+
* pointer events is drawn by the instance, imperatively, and Vue does not
|
|
9
|
+
* hear of it — its reactivity is the wrong tool for sixty frames a second of
|
|
10
|
+
* SVG attributes.
|
|
11
|
+
*
|
|
12
|
+
* What a component *does* render from is the instance's store.
|
|
13
|
+
* `useTimelineState` subscribes to it with a selector and gives a shallow
|
|
14
|
+
* ref: one that shows the selection changes when the selection changes and
|
|
15
|
+
* at no other time; one that shows the visible range changes once per drawn
|
|
16
|
+
* frame at most, however many events the frame held.
|
|
17
|
+
*
|
|
18
|
+
* **Server rendering.** On the server the component prints the same drawing
|
|
19
|
+
* the browser would draw (the core is DOM-free), so the page shows a
|
|
20
|
+
* timeline before the bundle loads; on hydration the instance takes those
|
|
21
|
+
* nodes over. The drawing goes into the server's markup only: Vue 3.5
|
|
22
|
+
* writes a dynamic `innerHTML` again while hydrating, and an `innerHTML`
|
|
23
|
+
* written by script is not the parser's — the instance would draw a second
|
|
24
|
+
* timeline. On the client the node has no children for Vue to compare, so
|
|
25
|
+
* Vue leaves alone what the parser built.
|
|
26
|
+
*/
|
|
27
|
+
import type { ItemSlot, Timeline as TimelineInstance, TimelineComponentConfig, TimelineInstanceEventMap, TimelineInstanceEventName, TimelineSnapshot } from "@temporeum/timeline-element";
|
|
28
|
+
import type { AllowedComponentProps, ComponentCustomProps, ComponentOptionsMixin, CreateComponentPublicInstanceWithMixins, MaybeRefOrGetter, PublicProps, ShallowRef, SlotsType, VNodeProps } from "vue";
|
|
29
|
+
export type { TimelineComponentConfig, TimelineConfig, TimelineSnapshot } from "@temporeum/timeline-element";
|
|
30
|
+
/** The element's plugin registry, from the adapter too: an app that installs only the adapter still registers the plugins its configuration needs. */
|
|
31
|
+
export type { ClusterOpenedPayload, Command, CommandPayload, DensityChangedPayload, Domain, Group, GroupMovedPayload, GroupToggledPayload, HoverPayload, InputSkippedPayload, Item, ItemEventPayload, ItemMovedPayload, ItemPatch, ItemResizedPayload, ItemsAddedPayload, ItemsChangedPayload, ItemsRemovedPayload, JumpRequestPayload, Lane, LayoutChangedPayload, OrientationChangedPayload, SelectionChangedPayload, SlotsChangedPayload, TimeBar, TimeBarMovedPayload, TimelineInstanceEventMap, TimelineInstanceEventName, TimeMs, TimeRange, ViewportChangedPayload, } from "@temporeum/timeline-element";
|
|
32
|
+
export { hasPlugin, registerPlugin } from "@temporeum/timeline-element";
|
|
33
|
+
export type { PluginName, TimelinePlugin } from "@temporeum/timeline-element";
|
|
34
|
+
export type { TimelineInstance };
|
|
35
|
+
/** What the component emits: every instance event, by its own name, and `ready` with the instance (`null` on unmount). */
|
|
36
|
+
export type TimelineEmits<T = unknown> = {
|
|
37
|
+
readonly [K in TimelineInstanceEventName]: (payload: TimelineInstanceEventMap<T>[K]) => void;
|
|
38
|
+
} & {
|
|
39
|
+
readonly ready: (timeline: TimelineInstance<T> | null) => void;
|
|
40
|
+
};
|
|
41
|
+
/** The component's props: every {@link TimelineComponentConfig} field, and the width the server draws at. */
|
|
42
|
+
export type TimelineProps<T = unknown> = TimelineComponentConfig<T> & {
|
|
43
|
+
/** Width the server draws at; the client redraws at the real one. */
|
|
44
|
+
readonly ssrWidth?: number | undefined;
|
|
45
|
+
};
|
|
46
|
+
/** The `#item` slot: each drawn item's content. */
|
|
47
|
+
export type TimelineSlots<T = unknown> = SlotsType<{
|
|
48
|
+
readonly item: {
|
|
49
|
+
readonly item: Item<T>;
|
|
50
|
+
};
|
|
51
|
+
}>;
|
|
52
|
+
/** A handler prop for every event — `onItemMoved`, `onReady`, … — its payload typed by the items' data. */
|
|
53
|
+
export type TimelineListeners<T = unknown> = {
|
|
54
|
+
readonly [K in keyof TimelineEmits<T> & string as `on${Capitalize<K>}`]?: TimelineEmits<T>[K];
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The component's type, generic over the items' data: every prop, every
|
|
58
|
+
* handler and the `#item` slot typed by it, and a template ref's `timeline`.
|
|
59
|
+
* Written out because Vue types the runtime `emits` list — the names — as
|
|
60
|
+
* handlers of `(...args: any[]) => any`, and the options' slots as `unknown`.
|
|
61
|
+
*/
|
|
62
|
+
export type TimelineComponent = new <T = unknown>(props: TimelineProps<T> & TimelineListeners<T> & VNodeProps & AllowedComponentProps & ComponentCustomProps) => CreateComponentPublicInstanceWithMixins<TimelineProps<T> & TimelineListeners<T>, {
|
|
63
|
+
readonly timeline: TimelineInstance<T> | null;
|
|
64
|
+
}, Record<never, never>, Record<never, never>, Record<never, never>, ComponentOptionsMixin, ComponentOptionsMixin, TimelineEmits<T>, PublicProps, Record<never, never>, false, Record<never, never>, TimelineSlots<T>>;
|
|
65
|
+
/** An item, as the element's types name it (the adapter depends on the element alone). */
|
|
66
|
+
type Item<T> = ItemSlot<T>["item"];
|
|
67
|
+
/**
|
|
68
|
+
* The timeline. Every {@link TimelineConfig} field is a prop (`:items`,
|
|
69
|
+
* `:time-zone` or `:timeZone`, a bare `lane-headers`), every instance event
|
|
70
|
+
* an emit (`@item-moved`, `@selection-changed`, …), and `ready` hands over
|
|
71
|
+
* the live instance; a template ref's `timeline` is the same instance.
|
|
72
|
+
* `class`, `style`, `id` and the rest of the attributes go on the box.
|
|
73
|
+
*
|
|
74
|
+
* The `#item` slot (`<template #item="{ item }">`) is each item's content
|
|
75
|
+
* as Vue, teleported into the box over the item (`slots`): only the items
|
|
76
|
+
* drawn have one. The content is a picture — the pointer and the keyboard
|
|
77
|
+
* act on the item, and its `label` stays its accessible name. Needs the
|
|
78
|
+
* `/items` plugin, as `template` does.
|
|
79
|
+
*/
|
|
80
|
+
export declare const Timeline: TimelineComponent;
|
|
81
|
+
/**
|
|
82
|
+
* Subscribe to the part of the instance's state a component renders.
|
|
83
|
+
*
|
|
84
|
+
* The ref changes when `selector`'s result changes (by `isEqual`,
|
|
85
|
+
* `Object.is` by default) — and at most once per drawn frame, because the
|
|
86
|
+
* store notifies at most once per frame. `undefined` until there is an
|
|
87
|
+
* instance; `timeline` may be a ref, a getter (`() => view.value?.timeline`)
|
|
88
|
+
* or the instance itself.
|
|
89
|
+
*/
|
|
90
|
+
export declare function useTimelineState<T, S>(timeline: MaybeRefOrGetter<TimelineInstance<T> | null | undefined>, selector: (snapshot: TimelineSnapshot) => S, isEqual?: (a: S, b: S) => boolean): Readonly<ShallowRef<S | undefined>>;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@temporeum/timeline-vue` — the timeline as a Vue component.
|
|
3
|
+
*
|
|
4
|
+
* The component is a shell around the shared instance (`createTimeline`,
|
|
5
|
+
* from `@temporeum/timeline-element`), as the React one is: it creates the
|
|
6
|
+
* instance once, on mount, hands it every attribute that changed, and lets
|
|
7
|
+
* go of it on unmount. It does not re-render to draw. A pan of a hundred
|
|
8
|
+
* pointer events is drawn by the instance, imperatively, and Vue does not
|
|
9
|
+
* hear of it — its reactivity is the wrong tool for sixty frames a second of
|
|
10
|
+
* SVG attributes.
|
|
11
|
+
*
|
|
12
|
+
* What a component *does* render from is the instance's store.
|
|
13
|
+
* `useTimelineState` subscribes to it with a selector and gives a shallow
|
|
14
|
+
* ref: one that shows the selection changes when the selection changes and
|
|
15
|
+
* at no other time; one that shows the visible range changes once per drawn
|
|
16
|
+
* frame at most, however many events the frame held.
|
|
17
|
+
*
|
|
18
|
+
* **Server rendering.** On the server the component prints the same drawing
|
|
19
|
+
* the browser would draw (the core is DOM-free), so the page shows a
|
|
20
|
+
* timeline before the bundle loads; on hydration the instance takes those
|
|
21
|
+
* nodes over. The drawing goes into the server's markup only: Vue 3.5
|
|
22
|
+
* writes a dynamic `innerHTML` again while hydrating, and an `innerHTML`
|
|
23
|
+
* written by script is not the parser's — the instance would draw a second
|
|
24
|
+
* timeline. On the client the node has no children for Vue to compare, so
|
|
25
|
+
* Vue leaves alone what the parser built.
|
|
26
|
+
*/
|
|
27
|
+
import type { ItemSlot, Timeline as TimelineInstance, TimelineComponentConfig, TimelineInstanceEventMap, TimelineInstanceEventName, TimelineSnapshot } from "@temporeum/timeline-element";
|
|
28
|
+
import type { AllowedComponentProps, ComponentCustomProps, ComponentOptionsMixin, CreateComponentPublicInstanceWithMixins, MaybeRefOrGetter, PublicProps, ShallowRef, SlotsType, VNodeProps } from "vue";
|
|
29
|
+
export type { TimelineComponentConfig, TimelineConfig, TimelineSnapshot } from "@temporeum/timeline-element";
|
|
30
|
+
/** The element's plugin registry, from the adapter too: an app that installs only the adapter still registers the plugins its configuration needs. */
|
|
31
|
+
export type { ClusterOpenedPayload, Command, CommandPayload, DensityChangedPayload, Domain, Group, GroupMovedPayload, GroupToggledPayload, HoverPayload, InputSkippedPayload, Item, ItemEventPayload, ItemMovedPayload, ItemPatch, ItemResizedPayload, ItemsAddedPayload, ItemsChangedPayload, ItemsRemovedPayload, JumpRequestPayload, Lane, LayoutChangedPayload, OrientationChangedPayload, SelectionChangedPayload, SlotsChangedPayload, TimeBar, TimeBarMovedPayload, TimelineInstanceEventMap, TimelineInstanceEventName, TimeMs, TimeRange, ViewportChangedPayload, } from "@temporeum/timeline-element";
|
|
32
|
+
export { hasPlugin, registerPlugin } from "@temporeum/timeline-element";
|
|
33
|
+
export type { PluginName, TimelinePlugin } from "@temporeum/timeline-element";
|
|
34
|
+
export type { TimelineInstance };
|
|
35
|
+
/** What the component emits: every instance event, by its own name, and `ready` with the instance (`null` on unmount). */
|
|
36
|
+
export type TimelineEmits<T = unknown> = {
|
|
37
|
+
readonly [K in TimelineInstanceEventName]: (payload: TimelineInstanceEventMap<T>[K]) => void;
|
|
38
|
+
} & {
|
|
39
|
+
readonly ready: (timeline: TimelineInstance<T> | null) => void;
|
|
40
|
+
};
|
|
41
|
+
/** The component's props: every {@link TimelineComponentConfig} field, and the width the server draws at. */
|
|
42
|
+
export type TimelineProps<T = unknown> = TimelineComponentConfig<T> & {
|
|
43
|
+
/** Width the server draws at; the client redraws at the real one. */
|
|
44
|
+
readonly ssrWidth?: number | undefined;
|
|
45
|
+
};
|
|
46
|
+
/** The `#item` slot: each drawn item's content. */
|
|
47
|
+
export type TimelineSlots<T = unknown> = SlotsType<{
|
|
48
|
+
readonly item: {
|
|
49
|
+
readonly item: Item<T>;
|
|
50
|
+
};
|
|
51
|
+
}>;
|
|
52
|
+
/** A handler prop for every event — `onItemMoved`, `onReady`, … — its payload typed by the items' data. */
|
|
53
|
+
export type TimelineListeners<T = unknown> = {
|
|
54
|
+
readonly [K in keyof TimelineEmits<T> & string as `on${Capitalize<K>}`]?: TimelineEmits<T>[K];
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The component's type, generic over the items' data: every prop, every
|
|
58
|
+
* handler and the `#item` slot typed by it, and a template ref's `timeline`.
|
|
59
|
+
* Written out because Vue types the runtime `emits` list — the names — as
|
|
60
|
+
* handlers of `(...args: any[]) => any`, and the options' slots as `unknown`.
|
|
61
|
+
*/
|
|
62
|
+
export type TimelineComponent = new <T = unknown>(props: TimelineProps<T> & TimelineListeners<T> & VNodeProps & AllowedComponentProps & ComponentCustomProps) => CreateComponentPublicInstanceWithMixins<TimelineProps<T> & TimelineListeners<T>, {
|
|
63
|
+
readonly timeline: TimelineInstance<T> | null;
|
|
64
|
+
}, Record<never, never>, Record<never, never>, Record<never, never>, ComponentOptionsMixin, ComponentOptionsMixin, TimelineEmits<T>, PublicProps, Record<never, never>, false, Record<never, never>, TimelineSlots<T>>;
|
|
65
|
+
/** An item, as the element's types name it (the adapter depends on the element alone). */
|
|
66
|
+
type Item<T> = ItemSlot<T>["item"];
|
|
67
|
+
/**
|
|
68
|
+
* The timeline. Every {@link TimelineConfig} field is a prop (`:items`,
|
|
69
|
+
* `:time-zone` or `:timeZone`, a bare `lane-headers`), every instance event
|
|
70
|
+
* an emit (`@item-moved`, `@selection-changed`, …), and `ready` hands over
|
|
71
|
+
* the live instance; a template ref's `timeline` is the same instance.
|
|
72
|
+
* `class`, `style`, `id` and the rest of the attributes go on the box.
|
|
73
|
+
*
|
|
74
|
+
* The `#item` slot (`<template #item="{ item }">`) is each item's content
|
|
75
|
+
* as Vue, teleported into the box over the item (`slots`): only the items
|
|
76
|
+
* drawn have one. The content is a picture — the pointer and the keyboard
|
|
77
|
+
* act on the item, and its `label` stays its accessible name. Needs the
|
|
78
|
+
* `/items` plugin, as `template` does.
|
|
79
|
+
*/
|
|
80
|
+
export declare const Timeline: TimelineComponent;
|
|
81
|
+
/**
|
|
82
|
+
* Subscribe to the part of the instance's state a component renders.
|
|
83
|
+
*
|
|
84
|
+
* The ref changes when `selector`'s result changes (by `isEqual`,
|
|
85
|
+
* `Object.is` by default) — and at most once per drawn frame, because the
|
|
86
|
+
* store notifies at most once per frame. `undefined` until there is an
|
|
87
|
+
* instance; `timeline` may be a ref, a getter (`() => view.value?.timeline`)
|
|
88
|
+
* or the instance itself.
|
|
89
|
+
*/
|
|
90
|
+
export declare function useTimelineState<T, S>(timeline: MaybeRefOrGetter<TimelineInstance<T> | null | undefined>, selector: (snapshot: TimelineSnapshot) => S, isEqual?: (a: S, b: S) => boolean): Readonly<ShallowRef<S | undefined>>;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/*! @temporeum/timeline-vue | MIT */
|
|
2
|
+
import { renderTimelineSvg, createTimeline, TIMELINE_EVENTS, DEFAULT_HEIGHT } from '@temporeum/timeline-element';
|
|
3
|
+
export { hasPlugin, registerPlugin } from '@temporeum/timeline-element';
|
|
4
|
+
import { defineComponent, useId, ref, shallowRef, toRaw, inject, ssrContextKey, onMounted, onUpdated, onBeforeUnmount, h, Teleport, watch, toValue } from 'vue';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `@temporeum/timeline-vue` — the timeline as a Vue component.
|
|
8
|
+
*
|
|
9
|
+
* The component is a shell around the shared instance (`createTimeline`,
|
|
10
|
+
* from `@temporeum/timeline-element`), as the React one is: it creates the
|
|
11
|
+
* instance once, on mount, hands it every attribute that changed, and lets
|
|
12
|
+
* go of it on unmount. It does not re-render to draw. A pan of a hundred
|
|
13
|
+
* pointer events is drawn by the instance, imperatively, and Vue does not
|
|
14
|
+
* hear of it — its reactivity is the wrong tool for sixty frames a second of
|
|
15
|
+
* SVG attributes.
|
|
16
|
+
*
|
|
17
|
+
* What a component *does* render from is the instance's store.
|
|
18
|
+
* `useTimelineState` subscribes to it with a selector and gives a shallow
|
|
19
|
+
* ref: one that shows the selection changes when the selection changes and
|
|
20
|
+
* at no other time; one that shows the visible range changes once per drawn
|
|
21
|
+
* frame at most, however many events the frame held.
|
|
22
|
+
*
|
|
23
|
+
* **Server rendering.** On the server the component prints the same drawing
|
|
24
|
+
* the browser would draw (the core is DOM-free), so the page shows a
|
|
25
|
+
* timeline before the bundle loads; on hydration the instance takes those
|
|
26
|
+
* nodes over. The drawing goes into the server's markup only: Vue 3.5
|
|
27
|
+
* writes a dynamic `innerHTML` again while hydrating, and an `innerHTML`
|
|
28
|
+
* written by script is not the parser's — the instance would draw a second
|
|
29
|
+
* timeline. On the client the node has no children for Vue to compare, so
|
|
30
|
+
* Vue leaves alone what the parser built.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* A prop that may be written as a bare attribute (`<Timeline lane-headers />`)
|
|
34
|
+
* is a boolean first: Vue reads the empty string as `true` for it. The rest
|
|
35
|
+
* take any value, and an absent one is `undefined` — not in the
|
|
36
|
+
* configuration, so the instance's default holds.
|
|
37
|
+
*/
|
|
38
|
+
const ANY = { type: null, default: undefined };
|
|
39
|
+
const FLAG = { type: Boolean, default: undefined };
|
|
40
|
+
const PROPS = {
|
|
41
|
+
items: ANY,
|
|
42
|
+
lanes: ANY,
|
|
43
|
+
groups: ANY,
|
|
44
|
+
template: ANY,
|
|
45
|
+
templateOptions: ANY,
|
|
46
|
+
slots: { type: [Boolean, Function], default: undefined },
|
|
47
|
+
gantt: ANY,
|
|
48
|
+
resources: ANY,
|
|
49
|
+
resourceOptions: ANY,
|
|
50
|
+
defaultDomain: ANY,
|
|
51
|
+
limits: ANY,
|
|
52
|
+
orientation: ANY,
|
|
53
|
+
breakpoint: ANY,
|
|
54
|
+
density: ANY,
|
|
55
|
+
metrics: ANY,
|
|
56
|
+
packMode: ANY,
|
|
57
|
+
theme: ANY,
|
|
58
|
+
locale: ANY,
|
|
59
|
+
timeZone: ANY,
|
|
60
|
+
formatTick: ANY,
|
|
61
|
+
axisContext: FLAG,
|
|
62
|
+
touchAction: ANY,
|
|
63
|
+
maxLabels: ANY,
|
|
64
|
+
labelOverflow: ANY,
|
|
65
|
+
ariaLabel: ANY,
|
|
66
|
+
decorations: ANY,
|
|
67
|
+
cluster: { type: [Boolean, Object], default: undefined },
|
|
68
|
+
hooks: ANY,
|
|
69
|
+
height: ANY,
|
|
70
|
+
interactions: ANY,
|
|
71
|
+
editing: ANY,
|
|
72
|
+
defaultSelectedIds: ANY,
|
|
73
|
+
instanceId: ANY,
|
|
74
|
+
nonce: ANY,
|
|
75
|
+
measureText: ANY,
|
|
76
|
+
sceneSpace: { type: [Boolean, String], default: undefined },
|
|
77
|
+
hiddenDates: ANY,
|
|
78
|
+
timeBars: ANY,
|
|
79
|
+
timeBarOptions: ANY,
|
|
80
|
+
rolling: { type: [Boolean, Object], default: undefined },
|
|
81
|
+
zoomPresets: { type: [Boolean, Array], default: undefined },
|
|
82
|
+
minimap: { type: [Boolean, Object], default: undefined },
|
|
83
|
+
rangeSelect: { type: [Boolean, Object], default: undefined },
|
|
84
|
+
laneHeaders: FLAG,
|
|
85
|
+
tooltip: { type: [Boolean, Function], default: undefined },
|
|
86
|
+
direction: ANY,
|
|
87
|
+
strings: ANY,
|
|
88
|
+
calendar: ANY,
|
|
89
|
+
ssrWidth: { type: Number, default: undefined },
|
|
90
|
+
};
|
|
91
|
+
/** Where the view and the selection start, and the server's width: said once, not configuration that changes. */
|
|
92
|
+
const OWN = new Set(["ssrWidth", "defaultDomain", "defaultSelectedIds"]);
|
|
93
|
+
/** `useId` gives `v-0` or `v:0`; a stylesheet scope wants a plain name. */
|
|
94
|
+
const scopeOf = (id) => `tl${id.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
95
|
+
/**
|
|
96
|
+
* The configuration part of the props: the ones given, as plain values. A
|
|
97
|
+
* reactive array or object the host passes is Vue's proxy of it; the
|
|
98
|
+
* instance holds the object itself, so its identity is the host's and the
|
|
99
|
+
* frames do not go through the proxy.
|
|
100
|
+
*/
|
|
101
|
+
function configOf(props, scope) {
|
|
102
|
+
const config = {};
|
|
103
|
+
for (const key of Object.keys(PROPS)) {
|
|
104
|
+
const value = props[key];
|
|
105
|
+
if (!OWN.has(key) && value !== undefined)
|
|
106
|
+
config[key] = toRaw(value);
|
|
107
|
+
}
|
|
108
|
+
// Light DOM: two timelines on one page must not share one stylesheet.
|
|
109
|
+
if (config.instanceId === undefined)
|
|
110
|
+
config.instanceId = scope;
|
|
111
|
+
return config;
|
|
112
|
+
}
|
|
113
|
+
/** The keys whose values are not the same object: an unchanged `items` array must not look like new data. */
|
|
114
|
+
function changedOf(before, next) {
|
|
115
|
+
const changed = {};
|
|
116
|
+
let any = false;
|
|
117
|
+
for (const key of new Set([...Object.keys(before), ...Object.keys(next)])) {
|
|
118
|
+
if (key !== "__proto__" && before[key] !== next[key]) {
|
|
119
|
+
changed[key] = next[key];
|
|
120
|
+
any = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return any ? changed : undefined;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The timeline. Every {@link TimelineConfig} field is a prop (`:items`,
|
|
127
|
+
* `:time-zone` or `:timeZone`, a bare `lane-headers`), every instance event
|
|
128
|
+
* an emit (`@item-moved`, `@selection-changed`, …), and `ready` hands over
|
|
129
|
+
* the live instance; a template ref's `timeline` is the same instance.
|
|
130
|
+
* `class`, `style`, `id` and the rest of the attributes go on the box.
|
|
131
|
+
*
|
|
132
|
+
* The `#item` slot (`<template #item="{ item }">`) is each item's content
|
|
133
|
+
* as Vue, teleported into the box over the item (`slots`): only the items
|
|
134
|
+
* drawn have one. The content is a picture — the pointer and the keyboard
|
|
135
|
+
* act on the item, and its `label` stays its accessible name. Needs the
|
|
136
|
+
* `/items` plugin, as `template` does.
|
|
137
|
+
*/
|
|
138
|
+
const Timeline = defineComponent((props, { emit, expose, slots }) => {
|
|
139
|
+
// Vue 3.5.0 types useId() as possibly undefined (outside a setup); here it is in one.
|
|
140
|
+
const scope = scopeOf(useId() ?? "");
|
|
141
|
+
const container = ref(null);
|
|
142
|
+
const timeline = shallowRef(null);
|
|
143
|
+
const drawn = shallowRef(new Map());
|
|
144
|
+
// With an `#item` slot every item has a box, unless `slots` picks.
|
|
145
|
+
const configNow = () => {
|
|
146
|
+
const config = configOf(props, scope);
|
|
147
|
+
if (slots.item !== undefined && config.slots === undefined)
|
|
148
|
+
config.slots = true;
|
|
149
|
+
return config;
|
|
150
|
+
};
|
|
151
|
+
let applied = configNow();
|
|
152
|
+
const initial = {
|
|
153
|
+
...applied,
|
|
154
|
+
...(props.defaultDomain === undefined ? {} : { domain: toRaw(props.defaultDomain) }),
|
|
155
|
+
...(props.defaultSelectedIds === undefined ? {} : { selectedIds: toRaw(props.defaultSelectedIds) }),
|
|
156
|
+
};
|
|
157
|
+
// The server's drawing, computed once — on the server only: Vue's
|
|
158
|
+
// server renderer is what provides an SSR context.
|
|
159
|
+
const html = inject(ssrContextKey, null) !== null
|
|
160
|
+
? (() => {
|
|
161
|
+
const frame = renderTimelineSvg(initial, props.ssrWidth === undefined ? {} : { width: props.ssrWidth });
|
|
162
|
+
return frame.svg + frame.groups;
|
|
163
|
+
})()
|
|
164
|
+
: undefined;
|
|
165
|
+
let stops = [];
|
|
166
|
+
onMounted(() => {
|
|
167
|
+
const instance = createTimeline(container.value, { ...initial, ...applied }, { hydrate: true });
|
|
168
|
+
stops = TIMELINE_EVENTS.map((type) => instance.on(type, (payload) => forward(emit, type, payload)));
|
|
169
|
+
// Vue renders the teleports in its own flush, a microtask after the
|
|
170
|
+
// frame that drew the boxes: before the browser paints it.
|
|
171
|
+
stops.push(instance.on("slotsChanged", ({ slots: next }) => (drawn.value = next)));
|
|
172
|
+
drawn.value = instance.slots;
|
|
173
|
+
timeline.value = instance;
|
|
174
|
+
emit("ready", instance);
|
|
175
|
+
});
|
|
176
|
+
// Hand the instance what changed, and only that.
|
|
177
|
+
onUpdated(() => {
|
|
178
|
+
const next = configNow();
|
|
179
|
+
const changed = changedOf(applied, next);
|
|
180
|
+
applied = next;
|
|
181
|
+
if (changed !== undefined)
|
|
182
|
+
timeline.value?.update(changed);
|
|
183
|
+
});
|
|
184
|
+
onBeforeUnmount(() => {
|
|
185
|
+
for (const stop of stops)
|
|
186
|
+
stop();
|
|
187
|
+
stops = [];
|
|
188
|
+
timeline.value?.destroy();
|
|
189
|
+
timeline.value = null;
|
|
190
|
+
emit("ready", null);
|
|
191
|
+
});
|
|
192
|
+
expose({ timeline });
|
|
193
|
+
return () => {
|
|
194
|
+
const height = props.height ?? DEFAULT_HEIGHT;
|
|
195
|
+
const item = slots.item;
|
|
196
|
+
const teleports = item === undefined ? [] : [...drawn.value].map(([id, slot]) => h(Teleport, { key: id, to: slot.element }, item({ item: slot.item })));
|
|
197
|
+
// The teleports are the box's children, as anchors: one root, so the
|
|
198
|
+
// server's markup stays the box alone. Without a teleport the box has
|
|
199
|
+
// no children for Vue — on the server it has the drawing, and while
|
|
200
|
+
// hydrating Vue must not compare the instance's nodes with any. The
|
|
201
|
+
// host's class, style and attributes fall through onto the box.
|
|
202
|
+
return h("div", {
|
|
203
|
+
ref: container,
|
|
204
|
+
style: { position: "relative", overflow: "hidden", height: `${String(height)}px` },
|
|
205
|
+
"data-tl-vue": "",
|
|
206
|
+
...(html === undefined ? {} : { innerHTML: html }),
|
|
207
|
+
}, html === undefined && teleports.length > 0 ? teleports : undefined);
|
|
208
|
+
};
|
|
209
|
+
}, {
|
|
210
|
+
name: "TemporeumTimeline",
|
|
211
|
+
// The runtime declaration; the types are the setup function's, generic over the items' data.
|
|
212
|
+
props: PROPS,
|
|
213
|
+
emits: [...TIMELINE_EVENTS, "ready"],
|
|
214
|
+
slots: Object,
|
|
215
|
+
});
|
|
216
|
+
/** One instance event, emitted under its own name: the payload is that event's. */
|
|
217
|
+
function forward(emit, type, payload) {
|
|
218
|
+
emit(type, payload);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Subscribe to the part of the instance's state a component renders.
|
|
222
|
+
*
|
|
223
|
+
* The ref changes when `selector`'s result changes (by `isEqual`,
|
|
224
|
+
* `Object.is` by default) — and at most once per drawn frame, because the
|
|
225
|
+
* store notifies at most once per frame. `undefined` until there is an
|
|
226
|
+
* instance; `timeline` may be a ref, a getter (`() => view.value?.timeline`)
|
|
227
|
+
* or the instance itself.
|
|
228
|
+
*/
|
|
229
|
+
function useTimelineState(timeline, selector, isEqual = Object.is) {
|
|
230
|
+
const state = shallowRef(undefined);
|
|
231
|
+
watch(() => toValue(timeline), (instance, _, onCleanup) => {
|
|
232
|
+
if (instance === null || instance === undefined) {
|
|
233
|
+
state.value = undefined;
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
let seen;
|
|
237
|
+
const read = () => {
|
|
238
|
+
const snapshot = instance.getSnapshot();
|
|
239
|
+
if (seen !== undefined && seen.snapshot === snapshot)
|
|
240
|
+
return;
|
|
241
|
+
const selected = selector(snapshot);
|
|
242
|
+
if (seen !== undefined && isEqual(seen.selected, selected)) {
|
|
243
|
+
seen = { snapshot, selected: seen.selected };
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
seen = { snapshot, selected };
|
|
247
|
+
state.value = selected;
|
|
248
|
+
};
|
|
249
|
+
read();
|
|
250
|
+
onCleanup(instance.subscribe(read));
|
|
251
|
+
}, { immediate: true });
|
|
252
|
+
return state;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export { Timeline, useTimelineState };
|
|
256
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["/**\n * `@temporeum/timeline-vue` — the timeline as a Vue component.\n *\n * The component is a shell around the shared instance (`createTimeline`,\n * from `@temporeum/timeline-element`), as the React one is: it creates the\n * instance once, on mount, hands it every attribute that changed, and lets\n * go of it on unmount. It does not re-render to draw. A pan of a hundred\n * pointer events is drawn by the instance, imperatively, and Vue does not\n * hear of it — its reactivity is the wrong tool for sixty frames a second of\n * SVG attributes.\n *\n * What a component *does* render from is the instance's store.\n * `useTimelineState` subscribes to it with a selector and gives a shallow\n * ref: one that shows the selection changes when the selection changes and\n * at no other time; one that shows the visible range changes once per drawn\n * frame at most, however many events the frame held.\n *\n * **Server rendering.** On the server the component prints the same drawing\n * the browser would draw (the core is DOM-free), so the page shows a\n * timeline before the bundle loads; on hydration the instance takes those\n * nodes over. The drawing goes into the server's markup only: Vue 3.5\n * writes a dynamic `innerHTML` again while hydrating, and an `innerHTML`\n * written by script is not the parser's — the instance would draw a second\n * timeline. On the client the node has no children for Vue to compare, so\n * Vue leaves alone what the parser built.\n */\n\nimport { createTimeline, DEFAULT_HEIGHT, renderTimelineSvg, TIMELINE_EVENTS } from \"@temporeum/timeline-element\";\nimport type {\n ItemSlot,\n Timeline as TimelineInstance,\n TimelineComponentConfig,\n TimelineConfig,\n TimelineInstanceEventMap,\n TimelineInstanceEventName,\n TimelineSnapshot,\n TimelineUpdate,\n} from \"@temporeum/timeline-element\";\nimport { defineComponent, h, inject, onBeforeUnmount, onMounted, onUpdated, ref, shallowRef, ssrContextKey, Teleport, toRaw, toValue, useId, watch } from \"vue\";\nimport type {\n AllowedComponentProps,\n ComponentCustomProps,\n ComponentOptionsMixin,\n CreateComponentPublicInstanceWithMixins,\n MaybeRefOrGetter,\n PublicProps,\n SetupContext,\n ShallowRef,\n SlotsType,\n VNodeProps,\n} from \"vue\";\n\nexport type { TimelineComponentConfig, TimelineConfig, TimelineSnapshot } from \"@temporeum/timeline-element\";\n/** The element's plugin registry, from the adapter too: an app that installs only the adapter still registers the plugins its configuration needs. */\n// The data and the event payloads, handed on: one install types an app.\nexport type {\n ClusterOpenedPayload,\n Command,\n CommandPayload,\n DensityChangedPayload,\n Domain,\n Group,\n GroupMovedPayload,\n GroupToggledPayload,\n HoverPayload,\n InputSkippedPayload,\n Item,\n ItemEventPayload,\n ItemMovedPayload,\n ItemPatch,\n ItemResizedPayload,\n ItemsAddedPayload,\n ItemsChangedPayload,\n ItemsRemovedPayload,\n JumpRequestPayload,\n Lane,\n LayoutChangedPayload,\n OrientationChangedPayload,\n SelectionChangedPayload,\n SlotsChangedPayload,\n TimeBar,\n TimeBarMovedPayload,\n TimelineInstanceEventMap,\n TimelineInstanceEventName,\n TimeMs,\n TimeRange,\n ViewportChangedPayload,\n} from \"@temporeum/timeline-element\";\nexport { hasPlugin, registerPlugin } from \"@temporeum/timeline-element\";\nexport type { PluginName, TimelinePlugin } from \"@temporeum/timeline-element\";\nexport type { TimelineInstance };\n\n/** What the component emits: every instance event, by its own name, and `ready` with the instance (`null` on unmount). */\nexport type TimelineEmits<T = unknown> = { readonly [K in TimelineInstanceEventName]: (payload: TimelineInstanceEventMap<T>[K]) => void } & {\n readonly ready: (timeline: TimelineInstance<T> | null) => void;\n};\n\n/** The component's props: every {@link TimelineComponentConfig} field, and the width the server draws at. */\nexport type TimelineProps<T = unknown> = TimelineComponentConfig<T> & {\n /** Width the server draws at; the client redraws at the real one. */\n readonly ssrWidth?: number | undefined;\n};\n\n/** The `#item` slot: each drawn item's content. */\nexport type TimelineSlots<T = unknown> = SlotsType<{ readonly item: { readonly item: Item<T> } }>;\n\n/** A handler prop for every event — `onItemMoved`, `onReady`, … — its payload typed by the items' data. */\nexport type TimelineListeners<T = unknown> = { readonly [K in keyof TimelineEmits<T> & string as `on${Capitalize<K>}`]?: TimelineEmits<T>[K] };\n\n/**\n * The component's type, generic over the items' data: every prop, every\n * handler and the `#item` slot typed by it, and a template ref's `timeline`.\n * Written out because Vue types the runtime `emits` list — the names — as\n * handlers of `(...args: any[]) => any`, and the options' slots as `unknown`.\n */\nexport type TimelineComponent = new <T = unknown>(\n props: TimelineProps<T> & TimelineListeners<T> & VNodeProps & AllowedComponentProps & ComponentCustomProps,\n) => CreateComponentPublicInstanceWithMixins<\n TimelineProps<T> & TimelineListeners<T>,\n { readonly timeline: TimelineInstance<T> | null },\n Record<never, never>,\n Record<never, never>,\n Record<never, never>,\n ComponentOptionsMixin,\n ComponentOptionsMixin,\n TimelineEmits<T>,\n PublicProps,\n Record<never, never>,\n false,\n Record<never, never>,\n TimelineSlots<T>\n>;\n\n/** An item, as the element's types name it (the adapter depends on the element alone). */\ntype Item<T> = ItemSlot<T>[\"item\"];\n\n/**\n * A prop that may be written as a bare attribute (`<Timeline lane-headers />`)\n * is a boolean first: Vue reads the empty string as `true` for it. The rest\n * take any value, and an absent one is `undefined` — not in the\n * configuration, so the instance's default holds.\n */\nconst ANY = { type: null, default: undefined } as const;\nconst FLAG = { type: Boolean, default: undefined } as const;\nconst PROPS: { readonly [K in keyof TimelineProps]-?: { readonly type: unknown; readonly default: undefined } } = {\n items: ANY,\n lanes: ANY,\n groups: ANY,\n template: ANY,\n templateOptions: ANY,\n slots: { type: [Boolean, Function], default: undefined },\n gantt: ANY,\n resources: ANY,\n resourceOptions: ANY,\n defaultDomain: ANY,\n limits: ANY,\n orientation: ANY,\n breakpoint: ANY,\n density: ANY,\n metrics: ANY,\n packMode: ANY,\n theme: ANY,\n locale: ANY,\n timeZone: ANY,\n formatTick: ANY,\n axisContext: FLAG,\n touchAction: ANY,\n maxLabels: ANY,\n labelOverflow: ANY,\n ariaLabel: ANY,\n decorations: ANY,\n cluster: { type: [Boolean, Object], default: undefined },\n hooks: ANY,\n height: ANY,\n interactions: ANY,\n editing: ANY,\n defaultSelectedIds: ANY,\n instanceId: ANY,\n nonce: ANY,\n measureText: ANY,\n sceneSpace: { type: [Boolean, String], default: undefined },\n hiddenDates: ANY,\n timeBars: ANY,\n timeBarOptions: ANY,\n rolling: { type: [Boolean, Object], default: undefined },\n zoomPresets: { type: [Boolean, Array], default: undefined },\n minimap: { type: [Boolean, Object], default: undefined },\n rangeSelect: { type: [Boolean, Object], default: undefined },\n laneHeaders: FLAG,\n tooltip: { type: [Boolean, Function], default: undefined },\n direction: ANY,\n strings: ANY,\n calendar: ANY,\n ssrWidth: { type: Number, default: undefined },\n};\n\n/** Where the view and the selection start, and the server's width: said once, not configuration that changes. */\nconst OWN = new Set([\"ssrWidth\", \"defaultDomain\", \"defaultSelectedIds\"]);\n\n/** `useId` gives `v-0` or `v:0`; a stylesheet scope wants a plain name. */\nconst scopeOf = (id: string): string => `tl${id.replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n\n/**\n * The configuration part of the props: the ones given, as plain values. A\n * reactive array or object the host passes is Vue's proxy of it; the\n * instance holds the object itself, so its identity is the host's and the\n * frames do not go through the proxy.\n */\nfunction configOf(props: Record<string, unknown>, scope: string): Record<string, unknown> {\n const config: Record<string, unknown> = {};\n for (const key of Object.keys(PROPS)) {\n const value = props[key];\n if (!OWN.has(key) && value !== undefined) config[key] = toRaw(value);\n }\n // Light DOM: two timelines on one page must not share one stylesheet.\n if (config.instanceId === undefined) config.instanceId = scope;\n return config;\n}\n\n/** The keys whose values are not the same object: an unchanged `items` array must not look like new data. */\nfunction changedOf(before: Record<string, unknown>, next: Record<string, unknown>): Record<string, unknown> | undefined {\n const changed: Record<string, unknown> = {};\n let any = false;\n for (const key of new Set([...Object.keys(before), ...Object.keys(next)])) {\n if (key !== \"__proto__\" && before[key] !== next[key]) {\n changed[key] = next[key];\n any = true;\n }\n }\n return any ? changed : undefined;\n}\n\n/**\n * The timeline. Every {@link TimelineConfig} field is a prop (`:items`,\n * `:time-zone` or `:timeZone`, a bare `lane-headers`), every instance event\n * an emit (`@item-moved`, `@selection-changed`, …), and `ready` hands over\n * the live instance; a template ref's `timeline` is the same instance.\n * `class`, `style`, `id` and the rest of the attributes go on the box.\n *\n * The `#item` slot (`<template #item=\"{ item }\">`) is each item's content\n * as Vue, teleported into the box over the item (`slots`): only the items\n * drawn have one. The content is a picture — the pointer and the keyboard\n * act on the item, and its `label` stays its accessible name. Needs the\n * `/items` plugin, as `template` does.\n */\nexport const Timeline: TimelineComponent = defineComponent(\n <T = unknown>(props: TimelineProps<T>, { emit, expose, slots }: SetupContext<TimelineEmits<T>, TimelineSlots>) => {\n // Vue 3.5.0 types useId() as possibly undefined (outside a setup); here it is in one.\n const scope = scopeOf(useId() ?? \"\");\n const container = ref<HTMLDivElement | null>(null);\n const timeline = shallowRef<TimelineInstance<T> | null>(null);\n const drawn = shallowRef<ReadonlyMap<string, ItemSlot<T>>>(new Map());\n // With an `#item` slot every item has a box, unless `slots` picks.\n const configNow = (): Record<string, unknown> => {\n const config = configOf(props as Record<string, unknown>, scope);\n if (slots.item !== undefined && config.slots === undefined) config.slots = true;\n return config;\n };\n let applied = configNow();\n const initial: Record<string, unknown> = {\n ...applied,\n ...(props.defaultDomain === undefined ? {} : { domain: toRaw(props.defaultDomain) }),\n ...(props.defaultSelectedIds === undefined ? {} : { selectedIds: toRaw(props.defaultSelectedIds) }),\n };\n // The server's drawing, computed once — on the server only: Vue's\n // server renderer is what provides an SSR context.\n const html =\n inject(ssrContextKey, null) !== null\n ? (() => {\n const frame = renderTimelineSvg(initial as TimelineConfig<T>, props.ssrWidth === undefined ? {} : { width: props.ssrWidth });\n return frame.svg + frame.groups;\n })()\n : undefined;\n let stops: (() => void)[] = [];\n\n onMounted(() => {\n const instance = createTimeline<T>(container.value as HTMLDivElement, { ...initial, ...applied } as TimelineConfig<T>, { hydrate: true });\n stops = TIMELINE_EVENTS.map((type) => instance.on(type, (payload) => forward(emit, type, payload)));\n // Vue renders the teleports in its own flush, a microtask after the\n // frame that drew the boxes: before the browser paints it.\n stops.push(instance.on(\"slotsChanged\", ({ slots: next }) => (drawn.value = next)));\n drawn.value = instance.slots;\n timeline.value = instance;\n emit(\"ready\", instance);\n });\n\n // Hand the instance what changed, and only that.\n onUpdated(() => {\n const next = configNow();\n const changed = changedOf(applied, next);\n applied = next;\n if (changed !== undefined) timeline.value?.update(changed as TimelineUpdate<T>);\n });\n\n onBeforeUnmount(() => {\n for (const stop of stops) stop();\n stops = [];\n timeline.value?.destroy();\n timeline.value = null;\n emit(\"ready\", null);\n });\n\n expose({ timeline });\n\n return () => {\n const height = props.height ?? DEFAULT_HEIGHT;\n const item = slots.item;\n const teleports = item === undefined ? [] : [...drawn.value].map(([id, slot]) => h(Teleport, { key: id, to: slot.element }, item({ item: slot.item })));\n // The teleports are the box's children, as anchors: one root, so the\n // server's markup stays the box alone. Without a teleport the box has\n // no children for Vue — on the server it has the drawing, and while\n // hydrating Vue must not compare the instance's nodes with any. The\n // host's class, style and attributes fall through onto the box.\n return h(\n \"div\",\n {\n ref: container,\n style: { position: \"relative\", overflow: \"hidden\", height: `${String(height)}px` },\n \"data-tl-vue\": \"\",\n ...(html === undefined ? {} : { innerHTML: html }),\n },\n html === undefined && teleports.length > 0 ? teleports : undefined,\n );\n };\n },\n {\n name: \"TemporeumTimeline\",\n // The runtime declaration; the types are the setup function's, generic over the items' data.\n props: PROPS as never,\n emits: [...TIMELINE_EVENTS, \"ready\"],\n slots: Object as TimelineSlots,\n },\n) as unknown as TimelineComponent;\n\n/** One instance event, emitted under its own name: the payload is that event's. */\nfunction forward<T, K extends TimelineInstanceEventName>(emit: SetupContext<TimelineEmits<T>>[\"emit\"], type: K, payload: TimelineInstanceEventMap<T>[K]): void {\n (emit as (type: K, payload: TimelineInstanceEventMap<T>[K]) => void)(type, payload);\n}\n\n/**\n * Subscribe to the part of the instance's state a component renders.\n *\n * The ref changes when `selector`'s result changes (by `isEqual`,\n * `Object.is` by default) — and at most once per drawn frame, because the\n * store notifies at most once per frame. `undefined` until there is an\n * instance; `timeline` may be a ref, a getter (`() => view.value?.timeline`)\n * or the instance itself.\n */\nexport function useTimelineState<T, S>(\n timeline: MaybeRefOrGetter<TimelineInstance<T> | null | undefined>,\n selector: (snapshot: TimelineSnapshot) => S,\n isEqual: (a: S, b: S) => boolean = Object.is,\n): Readonly<ShallowRef<S | undefined>> {\n const state = shallowRef<S | undefined>(undefined);\n watch(\n () => toValue(timeline),\n (instance, _, onCleanup) => {\n if (instance === null || instance === undefined) {\n state.value = undefined;\n return;\n }\n let seen: { snapshot: TimelineSnapshot; selected: S } | undefined;\n const read = (): void => {\n const snapshot = instance.getSnapshot();\n if (seen !== undefined && seen.snapshot === snapshot) return;\n const selected = selector(snapshot);\n if (seen !== undefined && isEqual(seen.selected, selected)) {\n seen = { snapshot, selected: seen.selected };\n return;\n }\n seen = { snapshot, selected };\n state.value = selected;\n };\n read();\n onCleanup(instance.subscribe(read));\n },\n { immediate: true },\n );\n return state;\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,74 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@temporeum/timeline-vue",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The Temporeum timeline as a Vue component: editable, accessible, server-rendered and hydrated.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Daniel Horak",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/horakdani/Temporeum_timeline_SDK.git",
|
|
10
|
+
"directory": "packages/vue"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://horakdani.github.io/Temporeum_timeline_SDK/",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/horakdani/Temporeum_timeline_SDK/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"timeline",
|
|
18
|
+
"vue",
|
|
19
|
+
"nuxt",
|
|
20
|
+
"gantt",
|
|
21
|
+
"editable",
|
|
22
|
+
"accessible",
|
|
23
|
+
"ssr"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public",
|
|
27
|
+
"provenance": true
|
|
28
|
+
},
|
|
29
|
+
"type": "module",
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.mjs",
|
|
33
|
+
"types": "./dist/index.d.cts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"import": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"default": "./dist/index.mjs"
|
|
39
|
+
},
|
|
40
|
+
"require": {
|
|
41
|
+
"types": "./dist/index.d.cts",
|
|
42
|
+
"default": "./dist/index.cjs"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"./package.json": "./package.json"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"README.md",
|
|
50
|
+
"CHANGELOG.md"
|
|
51
|
+
],
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=22.12.0"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@temporeum/timeline-element": "^1.0.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"vue": "^3.5.0"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json --emitDeclarationOnly && rollup -c && node ../../scripts/sdk_cjs_types.mjs",
|
|
63
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
64
|
+
"test": "npm run --silent build -w @temporeum/timeline-core && npm run --silent build -w @temporeum/timeline-element && npm run --silent build && npm run --silent typecheck && vitest run --coverage"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@vue/compiler-dom": "^3.5.44",
|
|
68
|
+
"@vitest/browser-playwright": "^5.1.0",
|
|
69
|
+
"@vitest/coverage-v8": "^5.1.0",
|
|
70
|
+
"typescript": "^6.0.3",
|
|
71
|
+
"vitest": "^5.1.0",
|
|
72
|
+
"vue": "^3.5.44"
|
|
73
|
+
}
|
|
74
|
+
}
|