@formicoidea/labre-core 0.46.2 → 0.47.1
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/dist/_pkgs/affine-block-attachment/configs/slash-menu.js +2 -2
- package/dist/_pkgs/affine-block-attachment/translations.js +2 -2
- package/dist/_pkgs/affine-block-data-view/columns/index.d.ts +1 -1
- package/dist/_pkgs/affine-block-database/data-source.d.ts +1 -1
- package/dist/_pkgs/affine-block-database/properties/converts.d.ts +4 -4
- package/dist/_pkgs/affine-block-database/properties/index.d.ts +1 -1
- package/dist/_pkgs/affine-block-embed/embed-html-block/configs/toolbar.js +1 -1
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/toolbar.d.ts +1 -1
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/toolbar.js +1 -1
- package/dist/_pkgs/affine-block-embed/translations.d.ts +1 -1
- package/dist/_pkgs/affine-block-embed/translations.js +2 -2
- package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/configs/slash-menu.js +6 -6
- package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/configs/toolbar.js +1 -1
- package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/configs/toolbar.js +3 -3
- package/dist/_pkgs/affine-block-embed-doc/translations.d.ts +1 -1
- package/dist/_pkgs/affine-block-embed-doc/translations.js +8 -8
- package/dist/_pkgs/affine-block-frame/commands.js +41 -1
- package/dist/_pkgs/affine-block-frame/frame-manager.d.ts +1 -1
- package/dist/_pkgs/affine-block-frame/frame-toolbar.js +2 -2
- package/dist/_pkgs/affine-block-frame/translations.js +1 -1
- package/dist/_pkgs/affine-block-frame/view.js +7 -2
- package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.d.ts +7 -0
- package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.js +15 -1
- package/dist/_pkgs/affine-block-note/translations.js +8 -8
- package/dist/_pkgs/affine-block-root/commands/copy-link-commands.js +2 -2
- package/dist/_pkgs/affine-block-root/configs/toolbar.d.ts +1 -1
- package/dist/_pkgs/affine-block-root/configs/toolbar.js +1 -1
- package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/misc.d.ts +1 -1
- package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.d.ts +1 -1
- package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.js +1 -1
- package/dist/_pkgs/affine-block-root/translations.d.ts +1 -1
- package/dist/_pkgs/affine-block-root/translations.js +2 -2
- package/dist/_pkgs/affine-block-surface/extensions/canvas-grid/commands.js +21 -7
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/commands.d.ts +2 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/commands.js +79 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.d.ts +18 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.js +53 -30
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/index.js +1 -0
- package/dist/_pkgs/affine-block-surface/extensions/interchange-import.js +1 -1
- package/dist/_pkgs/affine-block-surface/extensions/selection-pane/commands.js +1 -1
- package/dist/_pkgs/affine-block-surface/renderer/element-highlight-overlay.d.ts +8 -2
- package/dist/_pkgs/affine-block-surface/renderer/element-highlight-overlay.js +11 -13
- package/dist/_pkgs/affine-block-surface/view.js +11 -2
- package/dist/_pkgs/affine-block-surface-ref/translations.js +8 -8
- package/dist/_pkgs/affine-components/translations.js +6 -6
- package/dist/_pkgs/affine-fragment-doc-title/doc-title.d.ts +8 -0
- package/dist/_pkgs/affine-fragment-doc-title/doc-title.js +65 -4
- package/dist/_pkgs/affine-fragment-doc-title/effects.d.ts +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/effects.js +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/index.d.ts +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/index.js +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/format.d.ts +69 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/format.js +182 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/title-band.d.ts +62 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/title-band.js +649 -0
- package/dist/_pkgs/affine-fragment-doc-title/translations.d.ts +21 -0
- package/dist/_pkgs/affine-fragment-doc-title/translations.js +58 -0
- package/dist/_pkgs/affine-gfx-group/toolbar/config.d.ts +2 -2
- package/dist/_pkgs/affine-gfx-group/toolbar/config.js +2 -2
- package/dist/_pkgs/affine-gfx-group/translations.js +1 -1
- package/dist/_pkgs/affine-gfx-note/translations.js +2 -2
- package/dist/_pkgs/affine-shared/services/index.d.ts +2 -0
- package/dist/_pkgs/affine-shared/services/index.js +2 -0
- package/dist/_pkgs/affine-shared/services/linked-doc-creation-service.d.ts +1 -1
- package/dist/_pkgs/affine-shared/services/properties-panel-service.d.ts +50 -0
- package/dist/_pkgs/affine-shared/services/properties-panel-service.js +17 -0
- package/dist/_pkgs/affine-shared/services/title-band-service.d.ts +124 -0
- package/dist/_pkgs/affine-shared/services/title-band-service.js +17 -0
- package/dist/_pkgs/affine-shared/services/translation-service/chrome.d.ts +3 -3
- package/dist/_pkgs/affine-shared/services/translation-service/chrome.js +17 -17
- package/dist/_pkgs/affine-widget-edgeless-auto-connect/translations.js +2 -2
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/translations.js +1 -1
- package/dist/_pkgs/affine-widget-linked-doc/translations.js +3 -3
- package/dist/_pkgs/affine-widget-slash-menu/translations.js +1 -1
- package/dist/_pkgs/data-view/property-presets/converts.d.ts +2 -2
- package/dist/_pkgs/data-view/property-presets/index.d.ts +1 -1
- package/dist/_pkgs/data-view/property-presets/number/cell-renderer.d.ts +1 -1
- package/dist/_pkgs/data-view/property-presets/number/define.d.ts +1 -1
- package/dist/_pkgs/data-view/property-presets/number/types.d.ts +2 -2
- package/dist/_pkgs/data-view/property-presets/pure-index.d.ts +2 -1
- package/dist/_pkgs/data-view/property-presets/pure-index.js +4 -0
- package/dist/_pkgs/std/extension/command-registry.d.ts +50 -1
- package/dist/_pkgs/std/extension/command-registry.js +26 -7
- package/dist/commands.js +2 -1
- package/dist/host-panels.d.ts +83 -2
- package/dist/host-panels.js +82 -1
- package/dist/translations.js +1 -1
- package/package.json +1 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/file-exporter.d.ts +0 -26
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/file-exporter.js +0 -73
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
3
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
4
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
5
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
6
|
+
var _, done = false;
|
|
7
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
8
|
+
var context = {};
|
|
9
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
10
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
11
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
12
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
13
|
+
if (kind === "accessor") {
|
|
14
|
+
if (result === void 0) continue;
|
|
15
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
16
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
17
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
18
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
19
|
+
}
|
|
20
|
+
else if (_ = accept(result)) {
|
|
21
|
+
if (kind === "field") initializers.unshift(_);
|
|
22
|
+
else descriptor[key] = _;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
26
|
+
done = true;
|
|
27
|
+
};
|
|
28
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
29
|
+
var useValue = arguments.length > 2;
|
|
30
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
31
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
32
|
+
}
|
|
33
|
+
return useValue ? value : void 0;
|
|
34
|
+
};
|
|
35
|
+
import { menu, popMenu, popupTargetFromElement, } from '../../affine-components/context-menu/index.js';
|
|
36
|
+
import { DatePicker } from '../../affine-components/date-picker/index.js';
|
|
37
|
+
import { RefNodeSlotsProvider } from '../../affine-inline-reference/index.js';
|
|
38
|
+
import { DOC_UNTITLED, formatLocale, PropertiesPanelProvider, TitleBandProvider, translateKey, } from '../../affine-shared/services/index.js';
|
|
39
|
+
import { stopPropagation } from '../../affine-shared/utils/index.js';
|
|
40
|
+
import { SignalWatcher, WithDisposable } from '../../global/lit/index.js';
|
|
41
|
+
import { ShadowlessElement } from '../../std/index.js';
|
|
42
|
+
import { CheckBoxCheckSolidIcon, CheckBoxUnIcon, LinkedPageIcon, PlusIcon, SidebarIcon, ToggleDownIcon, ToggleRightIcon, } from '@blocksuite/icons/lit';
|
|
43
|
+
import { computed, signal } from '@preact/signals-core';
|
|
44
|
+
import { css, html, nothing } from 'lit';
|
|
45
|
+
import { property } from 'lit/decorators.js';
|
|
46
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
47
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
48
|
+
import { TITLE_BAND_ADD, TITLE_BAND_CLEAR, TITLE_BAND_COLLAPSE, TITLE_BAND_EMPTY, TITLE_BAND_EXPAND, TITLE_BAND_LABEL, TITLE_BAND_LOADING, TITLE_BAND_NOT_SHOWN, TITLE_BAND_OPEN_PANEL, } from '../translations.js';
|
|
49
|
+
import { chipEditorKind, chipOptions, chipValueView, parseChipNumber, } from './format.js';
|
|
50
|
+
export const LABRE_TITLE_BAND = 'labre-title-band';
|
|
51
|
+
const NO_PAGES = [];
|
|
52
|
+
/**
|
|
53
|
+
* The band of property chips under the page title and at the head of the
|
|
54
|
+
* page's note on the canvas (ADR 0035 §11, REQ-PR-1 and PR-7).
|
|
55
|
+
*
|
|
56
|
+
* One component for both views: `<doc-title>` mounts it under the title
|
|
57
|
+
* (`surface="page"`), `edgeless-page-block-title` at the head of the page's
|
|
58
|
+
* note (`surface="canvas"`). On the canvas it sits inside the note's own DOM,
|
|
59
|
+
* so it follows the zoom like the rest of the note (REQ-PR-7), and it is
|
|
60
|
+
* interactive when the note is, i.e. once the note is being edited.
|
|
61
|
+
*
|
|
62
|
+
* Fed by `TitleBandProvider` alone (§9d): no provider, nothing at all. The
|
|
63
|
+
* chips are the sheet's visible properties in the host's order, an empty one
|
|
64
|
+
* included (REQ-PR-1); a click opens the editor of the property's data-view
|
|
65
|
+
* type and hands the value back through `set`, showing the chip as pending
|
|
66
|
+
* until the host settles it. The band never writes to the document and never
|
|
67
|
+
* applies a value. The "+" is drawn when the host passes `add`; the panel
|
|
68
|
+
* button when a `PropertiesPanelProvider` is registered; the "Not shown in
|
|
69
|
+
* this document" row only on the canvas (the page view's row is the host's).
|
|
70
|
+
*/
|
|
71
|
+
let TitleBand = (() => {
|
|
72
|
+
let _classSuper = SignalWatcher(WithDisposable(ShadowlessElement));
|
|
73
|
+
let _std_decorators;
|
|
74
|
+
let _std_initializers = [];
|
|
75
|
+
let _std_extraInitializers = [];
|
|
76
|
+
let _docId_decorators;
|
|
77
|
+
let _docId_initializers = [];
|
|
78
|
+
let _docId_extraInitializers = [];
|
|
79
|
+
let _surface_decorators;
|
|
80
|
+
let _surface_initializers = [];
|
|
81
|
+
let _surface_extraInitializers = [];
|
|
82
|
+
return class TitleBand extends _classSuper {
|
|
83
|
+
static {
|
|
84
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
85
|
+
_std_decorators = [property({ attribute: false })];
|
|
86
|
+
_docId_decorators = [property({ attribute: false })];
|
|
87
|
+
_surface_decorators = [property({ attribute: false })];
|
|
88
|
+
__esDecorate(this, null, _std_decorators, { kind: "accessor", name: "std", static: false, private: false, access: { has: obj => "std" in obj, get: obj => obj.std, set: (obj, value) => { obj.std = value; } }, metadata: _metadata }, _std_initializers, _std_extraInitializers);
|
|
89
|
+
__esDecorate(this, null, _docId_decorators, { kind: "accessor", name: "docId", static: false, private: false, access: { has: obj => "docId" in obj, get: obj => obj.docId, set: (obj, value) => { obj.docId = value; } }, metadata: _metadata }, _docId_initializers, _docId_extraInitializers);
|
|
90
|
+
__esDecorate(this, null, _surface_decorators, { kind: "accessor", name: "surface", static: false, private: false, access: { has: obj => "surface" in obj, get: obj => obj.surface, set: (obj, value) => { obj.surface = value; } }, metadata: _metadata }, _surface_initializers, _surface_extraInitializers);
|
|
91
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
92
|
+
}
|
|
93
|
+
static { this.styles = css `
|
|
94
|
+
labre-title-band {
|
|
95
|
+
display: block;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.labre-title-band {
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: column;
|
|
101
|
+
gap: 6px;
|
|
102
|
+
font-family: var(--affine-font-family);
|
|
103
|
+
font-size: var(--affine-font-sm);
|
|
104
|
+
line-height: 20px;
|
|
105
|
+
color: var(--affine-text-primary-color);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.labre-title-band[data-surface='canvas'] {
|
|
109
|
+
padding-bottom: 12px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.labre-title-band-row {
|
|
113
|
+
display: flex;
|
|
114
|
+
flex-wrap: wrap;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 4px;
|
|
117
|
+
min-height: 28px;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.labre-title-band button {
|
|
121
|
+
all: unset;
|
|
122
|
+
box-sizing: border-box;
|
|
123
|
+
display: inline-flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
gap: 6px;
|
|
126
|
+
height: 28px;
|
|
127
|
+
max-width: 100%;
|
|
128
|
+
padding: 0 8px;
|
|
129
|
+
border-radius: 6px;
|
|
130
|
+
cursor: pointer;
|
|
131
|
+
color: inherit;
|
|
132
|
+
transition: background-color 150ms;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.labre-title-band button:hover {
|
|
136
|
+
background-color: var(--affine-hover-color);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.labre-title-band button:focus-visible {
|
|
140
|
+
outline: 2px solid var(--affine-primary-color);
|
|
141
|
+
outline-offset: 1px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.labre-title-band button[disabled] {
|
|
145
|
+
cursor: progress;
|
|
146
|
+
opacity: 0.6;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.labre-title-band button.static {
|
|
150
|
+
cursor: default;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.labre-title-band button.static:hover {
|
|
154
|
+
background-color: transparent;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.labre-title-band svg {
|
|
158
|
+
flex: none;
|
|
159
|
+
width: 16px;
|
|
160
|
+
height: 16px;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.labre-title-band-icon-button {
|
|
164
|
+
color: var(--affine-icon-color) !important;
|
|
165
|
+
padding: 0 6px !important;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.labre-title-band-chip-name {
|
|
169
|
+
color: var(--affine-text-secondary-color);
|
|
170
|
+
white-space: nowrap;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.labre-title-band-chip-value {
|
|
174
|
+
display: inline-flex;
|
|
175
|
+
align-items: center;
|
|
176
|
+
gap: 4px;
|
|
177
|
+
overflow: hidden;
|
|
178
|
+
text-overflow: ellipsis;
|
|
179
|
+
white-space: nowrap;
|
|
180
|
+
max-width: 240px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.labre-title-band-chip[data-empty='true'] .labre-title-band-chip-value {
|
|
184
|
+
color: var(--affine-placeholder-color);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.labre-title-band-tag {
|
|
188
|
+
display: inline-block;
|
|
189
|
+
padding: 0 6px;
|
|
190
|
+
border-radius: 4px;
|
|
191
|
+
max-width: 160px;
|
|
192
|
+
overflow: hidden;
|
|
193
|
+
text-overflow: ellipsis;
|
|
194
|
+
white-space: nowrap;
|
|
195
|
+
background-color: var(--affine-tag-white);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.labre-title-band-skeleton {
|
|
199
|
+
display: inline-block;
|
|
200
|
+
width: 96px;
|
|
201
|
+
height: 20px;
|
|
202
|
+
border-radius: 6px;
|
|
203
|
+
background-color: var(--affine-hover-color);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.labre-title-band-not-shown-label {
|
|
207
|
+
color: var(--affine-text-secondary-color);
|
|
208
|
+
padding-right: 4px;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@media (prefers-reduced-motion: reduce) {
|
|
212
|
+
.labre-title-band button {
|
|
213
|
+
transition: none;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* A 44px target under a coarse pointer, without changing the drawing. */
|
|
218
|
+
@media (pointer: coarse) {
|
|
219
|
+
.labre-title-band button {
|
|
220
|
+
position: relative;
|
|
221
|
+
}
|
|
222
|
+
.labre-title-band button::after {
|
|
223
|
+
content: '';
|
|
224
|
+
position: absolute;
|
|
225
|
+
inset: -8px 0;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
`; }
|
|
229
|
+
_pop(target, title, items) {
|
|
230
|
+
this._menu?.close();
|
|
231
|
+
const handler = popMenu(popupTargetFromElement(target), {
|
|
232
|
+
options: {
|
|
233
|
+
title: { text: title },
|
|
234
|
+
items,
|
|
235
|
+
onClose: () => {
|
|
236
|
+
if (this._menu === handler)
|
|
237
|
+
this._menu = null;
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
this._menu = handler;
|
|
242
|
+
return handler;
|
|
243
|
+
}
|
|
244
|
+
/** Read inside `render` it subscribes, through `SignalWatcher`. */
|
|
245
|
+
get _readonly() {
|
|
246
|
+
return this.std?.store.readonly$.value ?? true;
|
|
247
|
+
}
|
|
248
|
+
get _locale() {
|
|
249
|
+
return this.std ? formatLocale(this.std) : 'en-US';
|
|
250
|
+
}
|
|
251
|
+
_t(wording, count) {
|
|
252
|
+
if (!this.std)
|
|
253
|
+
return wording[1];
|
|
254
|
+
return translateKey(this.std, wording[0], wording[1], count === undefined ? undefined : { count });
|
|
255
|
+
}
|
|
256
|
+
_getSources(service) {
|
|
257
|
+
const cached = this._sources;
|
|
258
|
+
if (cached && cached.service === service && cached.docId === this.docId) {
|
|
259
|
+
return cached;
|
|
260
|
+
}
|
|
261
|
+
const docId = this.docId;
|
|
262
|
+
const sources = {
|
|
263
|
+
service,
|
|
264
|
+
docId,
|
|
265
|
+
sheet$: service.sheet$(docId),
|
|
266
|
+
collapsed$: service.collapsed$?.(docId) ?? null,
|
|
267
|
+
notShown$: this.surface === 'canvas' ? (service.notShown$?.(docId) ?? null) : null,
|
|
268
|
+
};
|
|
269
|
+
this._sources = sources;
|
|
270
|
+
return sources;
|
|
271
|
+
}
|
|
272
|
+
_isCollapsed(sources) {
|
|
273
|
+
return sources.collapsed$?.value ?? this._localCollapsed$.value;
|
|
274
|
+
}
|
|
275
|
+
_setCollapsed(sources, collapsed) {
|
|
276
|
+
this._localCollapsed$.value = collapsed;
|
|
277
|
+
sources.service.setCollapsed?.(sources.docId, collapsed);
|
|
278
|
+
}
|
|
279
|
+
_write(property, value) {
|
|
280
|
+
const sources = this._sources;
|
|
281
|
+
if (!sources || property.readonly || this.std?.store.readonly !== false)
|
|
282
|
+
return;
|
|
283
|
+
if (this._pending$.peek().has(property.id))
|
|
284
|
+
return;
|
|
285
|
+
const { id } = property;
|
|
286
|
+
const settle = () => {
|
|
287
|
+
const next = new Set(this._pending$.peek());
|
|
288
|
+
next.delete(id);
|
|
289
|
+
this._pending$.value = next;
|
|
290
|
+
};
|
|
291
|
+
this._pending$.value = new Set([...this._pending$.peek(), id]);
|
|
292
|
+
let written;
|
|
293
|
+
try {
|
|
294
|
+
written = sources.service.set(sources.docId, id, value);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
settle();
|
|
298
|
+
console.error('[title-band] set threw', error);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
// The host reports and rolls back a refused write; the band only stops
|
|
302
|
+
// showing it as pending.
|
|
303
|
+
Promise.resolve(written).then(settle, (error) => {
|
|
304
|
+
settle();
|
|
305
|
+
console.error('[title-band] set was refused', error);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
_tagLabel(tag) {
|
|
309
|
+
return html `<span
|
|
310
|
+
class="labre-title-band-tag"
|
|
311
|
+
style=${styleMap(tag.color ? { backgroundColor: tag.color } : {})}
|
|
312
|
+
>${tag.label}</span
|
|
313
|
+
>`;
|
|
314
|
+
}
|
|
315
|
+
_clearItem(prop) {
|
|
316
|
+
return menu.action({
|
|
317
|
+
name: this._t(TITLE_BAND_CLEAR),
|
|
318
|
+
select: () => this._write(prop, null),
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
_popSelect(target, prop) {
|
|
322
|
+
const items = chipOptions(prop).map(tag => menu.action({
|
|
323
|
+
name: tag.label,
|
|
324
|
+
label: () => this._tagLabel(tag),
|
|
325
|
+
isSelected: tag.id === prop.value,
|
|
326
|
+
select: () => {
|
|
327
|
+
if (tag.id !== prop.value)
|
|
328
|
+
this._write(prop, tag.id);
|
|
329
|
+
},
|
|
330
|
+
}));
|
|
331
|
+
if (prop.value != null)
|
|
332
|
+
items.push(this._clearItem(prop));
|
|
333
|
+
this._pop(target, prop.name, items);
|
|
334
|
+
}
|
|
335
|
+
_popMultiSelect(target, prop) {
|
|
336
|
+
// A draft, because the host's answer arrives later through `sheet$`: two
|
|
337
|
+
// ticks in one open menu must not both start from the stale value.
|
|
338
|
+
const draft$ = signal(Array.isArray(prop.value)
|
|
339
|
+
? prop.value.filter((id) => typeof id === 'string')
|
|
340
|
+
: []);
|
|
341
|
+
const items = chipOptions(prop).map(tag => menu.checkbox({
|
|
342
|
+
name: tag.label,
|
|
343
|
+
label: () => this._tagLabel(tag),
|
|
344
|
+
checked: computed(() => draft$.value.includes(tag.id)),
|
|
345
|
+
select: checked => {
|
|
346
|
+
const next = checked
|
|
347
|
+
? draft$.peek().filter(id => id !== tag.id)
|
|
348
|
+
: [...draft$.peek(), tag.id];
|
|
349
|
+
draft$.value = next;
|
|
350
|
+
this._write(prop, next);
|
|
351
|
+
return !checked;
|
|
352
|
+
},
|
|
353
|
+
}));
|
|
354
|
+
this._pop(target, prop.name, items);
|
|
355
|
+
}
|
|
356
|
+
_popDate(target, prop) {
|
|
357
|
+
let close = () => { };
|
|
358
|
+
const picker = new DatePicker();
|
|
359
|
+
picker.padding = 0;
|
|
360
|
+
picker.value = typeof prop.value === 'number' ? prop.value : Date.now();
|
|
361
|
+
picker.onChange = (date) => {
|
|
362
|
+
if (date.getTime() !== prop.value)
|
|
363
|
+
this._write(prop, date.getTime());
|
|
364
|
+
close();
|
|
365
|
+
};
|
|
366
|
+
picker.onClear = () => {
|
|
367
|
+
this._write(prop, null);
|
|
368
|
+
close();
|
|
369
|
+
};
|
|
370
|
+
picker.onEscape = () => close();
|
|
371
|
+
const items = [() => html `${picker}`];
|
|
372
|
+
const handler = this._pop(target, prop.name, items);
|
|
373
|
+
close = () => handler.close();
|
|
374
|
+
}
|
|
375
|
+
_popInput(target, prop, kind) {
|
|
376
|
+
const initial = typeof prop.value === 'string' || typeof prop.value === 'number'
|
|
377
|
+
? String(prop.value)
|
|
378
|
+
: '';
|
|
379
|
+
this._pop(target, prop.name, [
|
|
380
|
+
menu.input({
|
|
381
|
+
initialValue: initial,
|
|
382
|
+
onComplete: text => {
|
|
383
|
+
if (text === initial)
|
|
384
|
+
return;
|
|
385
|
+
if (kind === 'number') {
|
|
386
|
+
const parsed = parseChipNumber(text);
|
|
387
|
+
this._write(prop, Number.isNaN(parsed) ? null : parsed);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
this._write(prop, text.trim() === '' ? null : text);
|
|
391
|
+
},
|
|
392
|
+
}),
|
|
393
|
+
]);
|
|
394
|
+
}
|
|
395
|
+
_renderValue(prop) {
|
|
396
|
+
const view = chipValueView(prop, this._locale);
|
|
397
|
+
switch (view.kind) {
|
|
398
|
+
case 'checkbox':
|
|
399
|
+
return view.checked ? CheckBoxCheckSolidIcon() : CheckBoxUnIcon();
|
|
400
|
+
case 'tags':
|
|
401
|
+
return repeat(view.tags, tag => tag.id, tag => this._tagLabel(tag));
|
|
402
|
+
case 'text':
|
|
403
|
+
return view.text;
|
|
404
|
+
default:
|
|
405
|
+
return this._t(TITLE_BAND_EMPTY);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
_renderChip(prop, hasPanel) {
|
|
409
|
+
const pending = this._pending$.value.has(prop.id);
|
|
410
|
+
const editable = !this._readonly && chipEditorKind(prop) !== 'none';
|
|
411
|
+
const clickable = editable || hasPanel;
|
|
412
|
+
const view = chipValueView(prop, this._locale);
|
|
413
|
+
return html `<button
|
|
414
|
+
class="labre-title-band-chip ${clickable ? '' : 'static'}"
|
|
415
|
+
data-testid="title-band-chip"
|
|
416
|
+
data-property-id=${prop.id}
|
|
417
|
+
data-empty=${view.kind === 'empty' ? 'true' : 'false'}
|
|
418
|
+
aria-busy=${pending ? 'true' : 'false'}
|
|
419
|
+
?disabled=${pending || !clickable}
|
|
420
|
+
@click=${(event) => this._onChip(event, prop)}
|
|
421
|
+
>
|
|
422
|
+
<span class="labre-title-band-chip-name">${prop.name}</span>
|
|
423
|
+
<span class="labre-title-band-chip-value"
|
|
424
|
+
>${this._renderValue(prop)}</span
|
|
425
|
+
>
|
|
426
|
+
</button>`;
|
|
427
|
+
}
|
|
428
|
+
_renderTools(sources, hasPanel) {
|
|
429
|
+
const canAdd = !!sources.service.add && !this._readonly;
|
|
430
|
+
return html `${canAdd
|
|
431
|
+
? html `<button
|
|
432
|
+
class="labre-title-band-icon-button"
|
|
433
|
+
data-testid="title-band-add"
|
|
434
|
+
aria-label=${this._t(TITLE_BAND_ADD)}
|
|
435
|
+
title=${this._t(TITLE_BAND_ADD)}
|
|
436
|
+
@click=${(event) => {
|
|
437
|
+
event.stopPropagation();
|
|
438
|
+
sources.service.add?.(sources.docId);
|
|
439
|
+
}}
|
|
440
|
+
>
|
|
441
|
+
${PlusIcon()}
|
|
442
|
+
</button>`
|
|
443
|
+
: nothing}
|
|
444
|
+
${hasPanel
|
|
445
|
+
? html `<button
|
|
446
|
+
class="labre-title-band-icon-button"
|
|
447
|
+
data-testid="title-band-open-panel"
|
|
448
|
+
aria-label=${this._t(TITLE_BAND_OPEN_PANEL)}
|
|
449
|
+
title=${this._t(TITLE_BAND_OPEN_PANEL)}
|
|
450
|
+
@click=${(event) => {
|
|
451
|
+
event.stopPropagation();
|
|
452
|
+
this._openPanel();
|
|
453
|
+
}}
|
|
454
|
+
>
|
|
455
|
+
${SidebarIcon()}
|
|
456
|
+
</button>`
|
|
457
|
+
: nothing}`;
|
|
458
|
+
}
|
|
459
|
+
_renderSheet(sources) {
|
|
460
|
+
const state = sources.sheet$.value;
|
|
461
|
+
if (state.status === 'loading') {
|
|
462
|
+
return html `<div
|
|
463
|
+
class="labre-title-band-row"
|
|
464
|
+
role="status"
|
|
465
|
+
aria-busy="true"
|
|
466
|
+
aria-label=${this._t(TITLE_BAND_LOADING)}
|
|
467
|
+
data-testid="title-band-loading"
|
|
468
|
+
>
|
|
469
|
+
<span class="labre-title-band-skeleton"></span>
|
|
470
|
+
<span class="labre-title-band-skeleton"></span>
|
|
471
|
+
</div>`;
|
|
472
|
+
}
|
|
473
|
+
if (state.status !== 'ready')
|
|
474
|
+
return nothing;
|
|
475
|
+
const hasPanel = !!this.std?.getOptional(PropertiesPanelProvider);
|
|
476
|
+
const { properties } = state;
|
|
477
|
+
const canAdd = !!sources.service.add && !this._readonly;
|
|
478
|
+
if (properties.length === 0 && !canAdd && !hasPanel)
|
|
479
|
+
return nothing;
|
|
480
|
+
if (properties.length > 0 && this._isCollapsed(sources)) {
|
|
481
|
+
return html `<div
|
|
482
|
+
class="labre-title-band-row"
|
|
483
|
+
role="group"
|
|
484
|
+
aria-label=${this._t(TITLE_BAND_LABEL)}
|
|
485
|
+
>
|
|
486
|
+
<button
|
|
487
|
+
data-testid="title-band-expand"
|
|
488
|
+
aria-expanded="false"
|
|
489
|
+
@click=${(event) => {
|
|
490
|
+
event.stopPropagation();
|
|
491
|
+
this._setCollapsed(sources, false);
|
|
492
|
+
}}
|
|
493
|
+
>
|
|
494
|
+
${ToggleRightIcon()}
|
|
495
|
+
<span class="labre-title-band-chip-name"
|
|
496
|
+
>${this._t(TITLE_BAND_EXPAND, properties.length)}</span
|
|
497
|
+
>
|
|
498
|
+
</button>
|
|
499
|
+
${this._renderTools(sources, hasPanel)}
|
|
500
|
+
</div>`;
|
|
501
|
+
}
|
|
502
|
+
return html `<div
|
|
503
|
+
class="labre-title-band-row"
|
|
504
|
+
role="group"
|
|
505
|
+
aria-label=${this._t(TITLE_BAND_LABEL)}
|
|
506
|
+
>
|
|
507
|
+
${properties.length > 0
|
|
508
|
+
? html `<button
|
|
509
|
+
class="labre-title-band-icon-button"
|
|
510
|
+
data-testid="title-band-collapse"
|
|
511
|
+
aria-expanded="true"
|
|
512
|
+
aria-label=${this._t(TITLE_BAND_COLLAPSE)}
|
|
513
|
+
title=${this._t(TITLE_BAND_COLLAPSE)}
|
|
514
|
+
@click=${(event) => {
|
|
515
|
+
event.stopPropagation();
|
|
516
|
+
this._setCollapsed(sources, true);
|
|
517
|
+
}}
|
|
518
|
+
>
|
|
519
|
+
${ToggleDownIcon()}
|
|
520
|
+
</button>`
|
|
521
|
+
: nothing}
|
|
522
|
+
${repeat(properties, prop => prop.id, prop => this._renderChip(prop, hasPanel))}
|
|
523
|
+
${this._renderTools(sources, hasPanel)}
|
|
524
|
+
</div>`;
|
|
525
|
+
}
|
|
526
|
+
_renderNotShown(sources) {
|
|
527
|
+
const pages = sources.notShown$?.value ?? NO_PAGES;
|
|
528
|
+
const std = this.std;
|
|
529
|
+
const slots = std?.getOptional(RefNodeSlotsProvider);
|
|
530
|
+
// A link that opens nothing is a lie: no linked-document path, no row.
|
|
531
|
+
if (!std || !slots || pages.length === 0)
|
|
532
|
+
return nothing;
|
|
533
|
+
return html `<div
|
|
534
|
+
class="labre-title-band-row"
|
|
535
|
+
data-testid="title-band-not-shown"
|
|
536
|
+
>
|
|
537
|
+
<span class="labre-title-band-not-shown-label"
|
|
538
|
+
>${this._t(TITLE_BAND_NOT_SHOWN)}</span
|
|
539
|
+
>
|
|
540
|
+
${repeat(pages, page => page.docId, page => html `<button
|
|
541
|
+
data-testid="title-band-not-shown-page"
|
|
542
|
+
data-doc-id=${page.docId}
|
|
543
|
+
@click=${(event) => {
|
|
544
|
+
event.stopPropagation();
|
|
545
|
+
slots.docLinkClicked.next({
|
|
546
|
+
pageId: page.docId,
|
|
547
|
+
host: std.host,
|
|
548
|
+
event,
|
|
549
|
+
});
|
|
550
|
+
}}
|
|
551
|
+
>
|
|
552
|
+
${LinkedPageIcon()}
|
|
553
|
+
<span class="labre-title-band-chip-value"
|
|
554
|
+
>${page.title.trim() === ''
|
|
555
|
+
? this._t(DOC_UNTITLED)
|
|
556
|
+
: page.title}</span
|
|
557
|
+
>
|
|
558
|
+
</button>`)}
|
|
559
|
+
</div>`;
|
|
560
|
+
}
|
|
561
|
+
connectedCallback() {
|
|
562
|
+
super.connectedCallback();
|
|
563
|
+
// The band lives inside the title and inside the note: a key or a press
|
|
564
|
+
// on one of its buttons is the band's, not the title editor's (Enter would
|
|
565
|
+
// split the title) nor the canvas's (a press would start a drag).
|
|
566
|
+
this._disposables.addFromEvent(this, 'keydown', stopPropagation);
|
|
567
|
+
this._disposables.addFromEvent(this, 'pointerdown', stopPropagation);
|
|
568
|
+
this._disposables.add(() => this._menu?.close());
|
|
569
|
+
}
|
|
570
|
+
render() {
|
|
571
|
+
const std = this.std;
|
|
572
|
+
if (!std || !this.docId)
|
|
573
|
+
return nothing;
|
|
574
|
+
const service = std.getOptional(TitleBandProvider);
|
|
575
|
+
if (!service)
|
|
576
|
+
return nothing;
|
|
577
|
+
const sources = this._getSources(service);
|
|
578
|
+
const sheet = this._renderSheet(sources);
|
|
579
|
+
const notShown = this.surface === 'canvas' ? this._renderNotShown(sources) : nothing;
|
|
580
|
+
if (sheet === nothing && notShown === nothing)
|
|
581
|
+
return nothing;
|
|
582
|
+
return html `<div
|
|
583
|
+
class="labre-title-band"
|
|
584
|
+
data-testid="title-band"
|
|
585
|
+
data-surface=${this.surface}
|
|
586
|
+
>
|
|
587
|
+
${sheet} ${notShown}
|
|
588
|
+
</div>`;
|
|
589
|
+
}
|
|
590
|
+
#std_accessor_storage;
|
|
591
|
+
get std() { return this.#std_accessor_storage; }
|
|
592
|
+
set std(value) { this.#std_accessor_storage = value; }
|
|
593
|
+
#docId_accessor_storage;
|
|
594
|
+
get docId() { return this.#docId_accessor_storage; }
|
|
595
|
+
set docId(value) { this.#docId_accessor_storage = value; }
|
|
596
|
+
#surface_accessor_storage;
|
|
597
|
+
/** Where the band is drawn; only the canvas draws the "Not shown" row. */
|
|
598
|
+
get surface() { return this.#surface_accessor_storage; }
|
|
599
|
+
set surface(value) { this.#surface_accessor_storage = value; }
|
|
600
|
+
constructor() {
|
|
601
|
+
super(...arguments);
|
|
602
|
+
this._sources = null;
|
|
603
|
+
/** The editor menu open now, put away with the band. */
|
|
604
|
+
this._menu = null;
|
|
605
|
+
/** Collapse state when the host does not remember one. */
|
|
606
|
+
this._localCollapsed$ = signal(false);
|
|
607
|
+
/** Property ids whose `set` has not settled yet. */
|
|
608
|
+
this._pending$ = signal(new Set());
|
|
609
|
+
this._onChip = (event, prop) => {
|
|
610
|
+
event.stopPropagation();
|
|
611
|
+
const target = event.currentTarget;
|
|
612
|
+
switch (chipEditorKind(prop)) {
|
|
613
|
+
case 'toggle':
|
|
614
|
+
this._write(prop, prop.value !== true);
|
|
615
|
+
return;
|
|
616
|
+
case 'select':
|
|
617
|
+
this._popSelect(target, prop);
|
|
618
|
+
return;
|
|
619
|
+
case 'multi-select':
|
|
620
|
+
this._popMultiSelect(target, prop);
|
|
621
|
+
return;
|
|
622
|
+
case 'date':
|
|
623
|
+
this._popDate(target, prop);
|
|
624
|
+
return;
|
|
625
|
+
case 'number':
|
|
626
|
+
this._popInput(target, prop, 'number');
|
|
627
|
+
return;
|
|
628
|
+
case 'text':
|
|
629
|
+
this._popInput(target, prop, 'text');
|
|
630
|
+
return;
|
|
631
|
+
default:
|
|
632
|
+
this._openPanel();
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
this._openPanel = () => {
|
|
636
|
+
if (!this.std)
|
|
637
|
+
return;
|
|
638
|
+
this.std
|
|
639
|
+
.getOptional(PropertiesPanelProvider)
|
|
640
|
+
?.open({ scope: 'page', docId: this.docId });
|
|
641
|
+
};
|
|
642
|
+
this.#std_accessor_storage = __runInitializers(this, _std_initializers, null);
|
|
643
|
+
this.#docId_accessor_storage = (__runInitializers(this, _std_extraInitializers), __runInitializers(this, _docId_initializers, ''));
|
|
644
|
+
this.#surface_accessor_storage = (__runInitializers(this, _docId_extraInitializers), __runInitializers(this, _surface_initializers, 'page'));
|
|
645
|
+
__runInitializers(this, _surface_extraInitializers);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
})();
|
|
649
|
+
export { TitleBand };
|
|
@@ -9,5 +9,26 @@ import type { ChromeWording } from '../affine-shared/services/index.js';
|
|
|
9
9
|
* `content: attr(...)` can read.
|
|
10
10
|
*/
|
|
11
11
|
export declare const DOC_TITLE_PLACEHOLDER: ChromeWording;
|
|
12
|
+
/** The band's accessible name: the group of property chips. */
|
|
13
|
+
export declare const TITLE_BAND_LABEL: ChromeWording;
|
|
14
|
+
/** What an empty chip says after the property's name (REQ-PR-1). */
|
|
15
|
+
export declare const TITLE_BAND_EMPTY: ChromeWording;
|
|
16
|
+
/** The "+" (REQ-PR-4); what it does is the host's. */
|
|
17
|
+
export declare const TITLE_BAND_ADD: ChromeWording;
|
|
18
|
+
/** The button beside the "+" that opens the host's properties panel. */
|
|
19
|
+
export declare const TITLE_BAND_OPEN_PANEL: ChromeWording;
|
|
20
|
+
/** Folds the chips away. */
|
|
21
|
+
export declare const TITLE_BAND_COLLAPSE: ChromeWording;
|
|
22
|
+
/** The folded band's only button; `count` is the number of chips. */
|
|
23
|
+
export declare const TITLE_BAND_EXPAND: ChromeWording;
|
|
24
|
+
/** The accessible name of the skeleton drawn while the sheet loads. */
|
|
25
|
+
export declare const TITLE_BAND_LOADING: ChromeWording;
|
|
26
|
+
/** Clears a select or a date from its editor. */
|
|
27
|
+
export declare const TITLE_BAND_CLEAR: ChromeWording;
|
|
28
|
+
/**
|
|
29
|
+
* The note header's row of child pages the document holds but does not
|
|
30
|
+
* display (ADR 0035 §9d, canvas view only).
|
|
31
|
+
*/
|
|
32
|
+
export declare const TITLE_BAND_NOT_SHOWN: ChromeWording;
|
|
12
33
|
/** Every wording this package declares, in the order it renders them. */
|
|
13
34
|
export declare const DOC_TITLE_WORDINGS: readonly ChromeWording[];
|