@markmap/toolbar 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Gerald
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,5 @@
1
- # Temporary Holding Version
1
+ # @markmap/toolbar
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Zoom and fold controls for a Markmap view.
4
+
5
+ See the [Markmap README](https://github.com/markmap/markmap#readme) for how the packages fit together.
@@ -0,0 +1 @@
1
+ export * from './toolbar';
package/dist/index.js ADDED
@@ -0,0 +1,173 @@
1
+ // src/toolbar.ts
2
+ var SVG_NS = "http://www.w3.org/2000/svg";
3
+ /** Built-in buttons, usable by name in `items`. */
4
+ var builtinButtons = {
5
+ "zoom-in": {
6
+ label: "Zoom in",
7
+ icon: "M8 3v10M3 8h10",
8
+ disabled: (mm) => !mm.result || atLimit(mm.transform.k, mm.scaleLimits[1]),
9
+ onClick: (mm) => {
10
+ mm.zoomIn();
11
+ }
12
+ },
13
+ "zoom-out": {
14
+ label: "Zoom out",
15
+ icon: "M3 8h10",
16
+ disabled: (mm) => !mm.result || atLimit(mm.transform.k, mm.scaleLimits[0]),
17
+ onClick: (mm) => {
18
+ mm.zoomOut();
19
+ }
20
+ },
21
+ fit: {
22
+ label: "Fit to view",
23
+ icon: "M2 6V2h4M10 2h4v4M14 10v4h-4M6 14H2v-4",
24
+ disabled: (mm) => !mm.result,
25
+ onClick: (mm) => {
26
+ mm.fit();
27
+ }
28
+ },
29
+ "expand-all": {
30
+ label: "Expand all",
31
+ icon: "M4 6l4-4 4 4M4 10l4 4 4-4",
32
+ disabled: (mm) => !mm.foldedIds.size,
33
+ onClick: (mm) => {
34
+ mm.expandAll();
35
+ }
36
+ }
37
+ };
38
+ var defaultToolbarItems = [
39
+ "zoom-in",
40
+ "zoom-out",
41
+ "fit",
42
+ "expand-all",
43
+ "fold-level"
44
+ ];
45
+ var CSS = `
46
+ .markmap-toolbar {
47
+ display: inline-flex;
48
+ align-items: center;
49
+ gap: 2px;
50
+ padding: 3px;
51
+ border: 1px solid color-mix(in srgb, var(--mm-text, #333) 20%, transparent);
52
+ border-radius: 6px;
53
+ background: var(--mm-bg, #fff);
54
+ color: var(--mm-text, #333);
55
+ font: 13px system-ui, sans-serif;
56
+ }
57
+ .markmap-toolbar button, .markmap-toolbar select {
58
+ height: 26px;
59
+ border: 0;
60
+ border-radius: 4px;
61
+ background: none;
62
+ color: inherit;
63
+ font: inherit;
64
+ cursor: pointer;
65
+ }
66
+ .markmap-toolbar button { min-width: 26px; padding: 0 5px; }
67
+ .markmap-toolbar button:hover:enabled, .markmap-toolbar select:hover:enabled {
68
+ background: color-mix(in srgb, var(--mm-text, #333) 12%, transparent);
69
+ }
70
+ .markmap-toolbar button:disabled, .markmap-toolbar select:disabled { opacity: 0.35; cursor: default; }
71
+ .markmap-toolbar :focus-visible { outline: 2px solid var(--mm-link, #0097e6); outline-offset: -2px; }
72
+ .markmap-toolbar option { background: Canvas; color: CanvasText; }
73
+ .markmap-toolbar svg { display: block; margin: auto; width: 16px; height: 16px; }
74
+ .markmap-toolbar path { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
75
+ `;
76
+ /** Scales only get close to a limit once animations and floats are done with them. */
77
+ var atLimit = (scale, limit) => Math.abs(scale / limit - 1) < 1e-6;
78
+ function createIcon(doc, path) {
79
+ const svg = doc.createElementNS(SVG_NS, "svg");
80
+ svg.setAttribute("viewBox", "0 0 16 16");
81
+ svg.setAttribute("aria-hidden", "true");
82
+ const shape = doc.createElementNS(SVG_NS, "path");
83
+ shape.setAttribute("d", path);
84
+ svg.append(shape);
85
+ return svg;
86
+ }
87
+ /** Depth of the deepest node with children: folding deeper than that does nothing. */
88
+ function deepestParent(node, depth = 0) {
89
+ if (!node?.children.length) return 0;
90
+ return Math.max(depth, ...node.children.map((child) => deepestParent(child, depth + 1)));
91
+ }
92
+ /**
93
+ * Controls for a map, in plain DOM. They use the public view API, follow its events, and
94
+ * take their colors from the map's theme.
95
+ */
96
+ function createToolbar(mm, options = {}) {
97
+ const doc = mm.element.ownerDocument;
98
+ const element = doc.createElement("div");
99
+ element.className = `markmap-toolbar ${mm.themeClass}`;
100
+ element.setAttribute("role", "toolbar");
101
+ element.setAttribute("aria-label", "Map controls");
102
+ element.setAttribute("part", "toolbar");
103
+ const style = doc.createElement("style");
104
+ style.textContent = CSS;
105
+ element.append(style);
106
+ const buttons = [];
107
+ let levels;
108
+ const updateButtons = () => {
109
+ for (const { el, item } of buttons) el.disabled = item.disabled?.(mm) ?? false;
110
+ };
111
+ const updateLevels = () => {
112
+ if (!levels) return;
113
+ const deepest = deepestParent(mm.tree);
114
+ levels.disabled = !deepest;
115
+ if (levels.options.length === deepest + 1) return;
116
+ const choices = Array.from({ length: deepest + 1 }, (_, i) => {
117
+ const option = doc.createElement("option");
118
+ option.value = i ? `${i}` : "";
119
+ option.textContent = i ? `${i}` : "Level";
120
+ return option;
121
+ });
122
+ levels.replaceChildren(...choices);
123
+ };
124
+ const update = () => {
125
+ updateButtons();
126
+ updateLevels();
127
+ };
128
+ for (const entry of options.items ?? defaultToolbarItems) {
129
+ if (entry === "fold-level") {
130
+ const select = doc.createElement("select");
131
+ select.title = "Fold to level";
132
+ select.setAttribute("aria-label", select.title);
133
+ select.addEventListener("change", () => {
134
+ if (select.value) mm.foldToLevel(Number(select.value));
135
+ select.value = "";
136
+ });
137
+ levels = select;
138
+ element.append(select);
139
+ continue;
140
+ }
141
+ const item = typeof entry === "string" ? builtinButtons[entry] : entry;
142
+ const el = doc.createElement("button");
143
+ el.type = "button";
144
+ el.title = item.label;
145
+ el.setAttribute("aria-label", item.label);
146
+ if (item.icon) el.append(createIcon(doc, item.icon));
147
+ else el.textContent = item.label;
148
+ el.addEventListener("click", () => {
149
+ item.onClick(mm);
150
+ update();
151
+ });
152
+ buttons.push({
153
+ el,
154
+ item
155
+ });
156
+ element.append(el);
157
+ }
158
+ const stop = [
159
+ mm.on("zoom", updateButtons),
160
+ mm.on("fold", updateButtons),
161
+ mm.on("render", update)
162
+ ];
163
+ update();
164
+ return {
165
+ element,
166
+ update,
167
+ destroy() {
168
+ for (const off of stop) off();
169
+ element.remove();
170
+ }
171
+ };
172
+ }
173
+ export { builtinButtons, createToolbar, defaultToolbarItems };
@@ -0,0 +1,58 @@
1
+ import type { Markmap } from '@markmap/view';
2
+ /** A button of your own, or a changed built-in: `{ ...builtinButtons.fit, label: 'Reset' }`. */
3
+ export interface ToolbarButton {
4
+ /** Names the button for screen readers and in its tooltip. Shown if there is no `icon`. */
5
+ label: string;
6
+ /** Path data of a line drawing in a 16 × 16 box. */
7
+ icon?: string;
8
+ /** Checked whenever the map changes. */
9
+ disabled?: (mm: Markmap) => boolean;
10
+ onClick: (mm: Markmap) => void;
11
+ }
12
+ /** Built-in buttons, usable by name in `items`. */
13
+ export declare const builtinButtons: {
14
+ 'zoom-in': {
15
+ label: string;
16
+ icon: string;
17
+ disabled: (mm: Markmap) => boolean;
18
+ onClick: (mm: Markmap) => void;
19
+ };
20
+ 'zoom-out': {
21
+ label: string;
22
+ icon: string;
23
+ disabled: (mm: Markmap) => boolean;
24
+ onClick: (mm: Markmap) => void;
25
+ };
26
+ fit: {
27
+ label: string;
28
+ icon: string;
29
+ disabled: (mm: Markmap) => boolean;
30
+ onClick: (mm: Markmap) => void;
31
+ };
32
+ 'expand-all': {
33
+ label: string;
34
+ icon: string;
35
+ disabled: (mm: Markmap) => boolean;
36
+ onClick: (mm: Markmap) => void;
37
+ };
38
+ };
39
+ /** A built-in by name, `fold-level` for the level menu, or a button of your own. */
40
+ export type ToolbarItem = keyof typeof builtinButtons | 'fold-level' | ToolbarButton;
41
+ export declare const defaultToolbarItems: readonly ToolbarItem[];
42
+ export interface ToolbarOptions {
43
+ /** What to show, in order. Defaults to `defaultToolbarItems`. */
44
+ items?: readonly ToolbarItem[];
45
+ }
46
+ export interface Toolbar {
47
+ /** Put this on the page. It is not positioned; style `.markmap-toolbar` to place it. */
48
+ readonly element: HTMLDivElement;
49
+ /** Check what is enabled now. Needed only when a custom `disabled` depends on other things. */
50
+ update(): void;
51
+ /** Stop following the map and remove the element. */
52
+ destroy(): void;
53
+ }
54
+ /**
55
+ * Controls for a map, in plain DOM. They use the public view API, follow its events, and
56
+ * take their colors from the map's theme.
57
+ */
58
+ export declare function createToolbar(mm: Markmap, options?: ToolbarOptions): Toolbar;
package/package.json CHANGED
@@ -1,6 +1,40 @@
1
1
  {
2
2
  "name": "@markmap/toolbar",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.0",
4
+ "description": "Zoom and fold controls for a Markmap view",
5
+ "homepage": "https://github.com/markmap/markmap#readme",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/markmap/markmap.git",
10
+ "directory": "packages/toolbar"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "type": "module",
16
+ "sideEffects": false,
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ }
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "devDependencies": {
27
+ "@markmap/parse": "1.0.0",
28
+ "@markmap/render": "1.0.0",
29
+ "@markmap/view": "1.0.0",
30
+ "happy-dom": "^21.0.4"
31
+ },
32
+ "peerDependencies": {
33
+ "@markmap/view": "^1.0.0"
34
+ },
35
+ "scripts": {
36
+ "clean": "rm -rf dist tsconfig.tsbuildinfo",
37
+ "build:types": "tsc -b",
38
+ "build:js": "vite build"
39
+ }
6
40
  }