@formicoidea/labre-core 0.46.2 → 0.47.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/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-frame/commands.js +41 -1
- package/dist/_pkgs/affine-block-frame/frame-manager.d.ts +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-surface/extensions/canvas-grid/commands.js +20 -6
- 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/view.js +11 -2
- 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-shared/services/index.d.ts +2 -0
- package/dist/_pkgs/affine-shared/services/index.js +2 -0
- 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/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/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
|
@@ -34,12 +34,12 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
34
34
|
};
|
|
35
35
|
import { CodeBlockModel, ListBlockModel, NoteBlockModel, NoteDisplayMode, ParagraphBlockModel, } from '../affine-model/index.js';
|
|
36
36
|
import { focusTextModel } from '../affine-rich-text/index.js';
|
|
37
|
-
import { translateKey } from '../affine-shared/services/index.js';
|
|
37
|
+
import { TitleBandProvider, translateKey } from '../affine-shared/services/index.js';
|
|
38
38
|
import { matchModels } from '../affine-shared/utils/index.js';
|
|
39
39
|
import { WithDisposable } from '../global/lit/index.js';
|
|
40
40
|
import { ShadowlessElement } from '../std/index.js';
|
|
41
41
|
import { effect } from '@preact/signals-core';
|
|
42
|
-
import { css, html } from 'lit';
|
|
42
|
+
import { css, html, nothing } from 'lit';
|
|
43
43
|
import { property, query, state } from 'lit/decorators.js';
|
|
44
44
|
import { DOC_TITLE_PLACEHOLDER } from './translations.js';
|
|
45
45
|
const DOC_BLOCK_CHILD_PADDING = 24;
|
|
@@ -76,9 +76,18 @@ let DocTitle = (() => {
|
|
|
76
76
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
77
77
|
}
|
|
78
78
|
static { this.styles = css `
|
|
79
|
+
/*
|
|
80
|
+
* The page title's family is a host's to choose (labre ADR-0049 §5: the
|
|
81
|
+
* Labre app sets Cabinet Grotesk); headings H1 to H6 stay in the body
|
|
82
|
+
* family. Unset, the title keeps the body family, DESIGN.md's One Family
|
|
83
|
+
* Rule.
|
|
84
|
+
*/
|
|
79
85
|
.doc-title-container {
|
|
80
86
|
box-sizing: border-box;
|
|
81
|
-
font-family: var(
|
|
87
|
+
font-family: var(
|
|
88
|
+
--labre-doc-title-font-family,
|
|
89
|
+
var(--affine-font-family)
|
|
90
|
+
);
|
|
82
91
|
font-size: var(--affine-font-base);
|
|
83
92
|
line-height: var(--affine-line-height);
|
|
84
93
|
color: var(--affine-text-primary-color);
|
|
@@ -112,6 +121,27 @@ let DocTitle = (() => {
|
|
|
112
121
|
}
|
|
113
122
|
}
|
|
114
123
|
|
|
124
|
+
.doc-title-container.doc-title-container-with-band {
|
|
125
|
+
padding-bottom: 8px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.doc-title-band {
|
|
129
|
+
box-sizing: border-box;
|
|
130
|
+
width: 100%;
|
|
131
|
+
max-width: var(--affine-editor-width);
|
|
132
|
+
margin-left: auto;
|
|
133
|
+
margin-right: auto;
|
|
134
|
+
padding: 0 var(--affine-editor-side-padding, ${DOC_BLOCK_CHILD_PADDING}px)
|
|
135
|
+
24px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@container viewport (width <= 640px) {
|
|
139
|
+
.doc-title-band {
|
|
140
|
+
padding-left: ${DOC_BLOCK_CHILD_PADDING}px;
|
|
141
|
+
padding-right: ${DOC_BLOCK_CHILD_PADDING}px;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
115
145
|
.doc-title-container-empty::before {
|
|
116
146
|
content: attr(data-placeholder);
|
|
117
147
|
color: var(--affine-placeholder-color);
|
|
@@ -178,13 +208,35 @@ let DocTitle = (() => {
|
|
|
178
208
|
});
|
|
179
209
|
}
|
|
180
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* The `std` the title band reads its seams from, when the band belongs
|
|
213
|
+
* here: in the page view, never inside a note on the canvas, where
|
|
214
|
+
* `edgeless-page-block-title` draws the band itself (ADR 0035 §11), so a
|
|
215
|
+
* host that renders this title in the note header does not get two.
|
|
216
|
+
*/
|
|
217
|
+
get _bandStd() {
|
|
218
|
+
if (this.closest('edgeless-page-block-title'))
|
|
219
|
+
return null;
|
|
220
|
+
const std = this._std;
|
|
221
|
+
return std?.getOptional(TitleBandProvider) ? std : null;
|
|
222
|
+
}
|
|
223
|
+
firstUpdated() {
|
|
224
|
+
// `<doc-title>` sits beside the editor host, not inside it: on the first
|
|
225
|
+
// frame the host may not carry its `std` yet, and without one the band
|
|
226
|
+
// cannot read its seam. One more render once it has mounted.
|
|
227
|
+
if (this._std)
|
|
228
|
+
return;
|
|
229
|
+
const frame = requestAnimationFrame(() => this.requestUpdate());
|
|
230
|
+
this._disposables.add(() => cancelAnimationFrame(frame));
|
|
231
|
+
}
|
|
181
232
|
render() {
|
|
182
233
|
const isEmpty = !this._rootModel?.props.title.length && !this._isComposing;
|
|
234
|
+
const bandStd = this._bandStd;
|
|
183
235
|
return html `
|
|
184
236
|
<div
|
|
185
237
|
class="doc-title-container ${isEmpty
|
|
186
238
|
? 'doc-title-container-empty'
|
|
187
|
-
: ''}"
|
|
239
|
+
: ''} ${bandStd ? 'doc-title-container-with-band' : ''}"
|
|
188
240
|
data-block-is-title="true"
|
|
189
241
|
data-placeholder=${this._placeholder}
|
|
190
242
|
>
|
|
@@ -197,6 +249,15 @@ let DocTitle = (() => {
|
|
|
197
249
|
.wrapText=${this.wrapText}
|
|
198
250
|
></rich-text>
|
|
199
251
|
</div>
|
|
252
|
+
${bandStd
|
|
253
|
+
? html `<div class="doc-title-band">
|
|
254
|
+
<labre-title-band
|
|
255
|
+
.std=${bandStd}
|
|
256
|
+
.docId=${this.doc.id}
|
|
257
|
+
.surface=${'page'}
|
|
258
|
+
></labre-title-band>
|
|
259
|
+
</div>`
|
|
260
|
+
: nothing}
|
|
200
261
|
`;
|
|
201
262
|
}
|
|
202
263
|
#_isComposing_accessor_storage;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { DocTitle } from './doc-title.js';
|
|
2
|
+
import { LABRE_TITLE_BAND, TitleBand } from './title-band/title-band.js';
|
|
2
3
|
export declare function effects(): void;
|
|
3
4
|
declare global {
|
|
4
5
|
interface HTMLElementTagNameMap {
|
|
5
6
|
'doc-title': DocTitle;
|
|
7
|
+
[LABRE_TITLE_BAND]: TitleBand;
|
|
6
8
|
}
|
|
7
9
|
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { DocTitle } from './doc-title.js';
|
|
2
|
+
export { chipEditorKind, type ChipEditorKind, type ChipTag, chipValueView, type ChipValueView, formatPropertyValue, isPropertyValueEmpty, } from './title-band/format.js';
|
|
3
|
+
export { LABRE_TITLE_BAND, TitleBand } from './title-band/title-band.js';
|
|
2
4
|
export { getDocTitleByEditorHost } from './utils.js';
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { DocTitle } from './doc-title.js';
|
|
2
|
+
export { chipEditorKind, chipValueView, formatPropertyValue, isPropertyValueEmpty, } from './title-band/format.js';
|
|
3
|
+
export { LABRE_TITLE_BAND, TitleBand } from './title-band/title-band.js';
|
|
2
4
|
export { getDocTitleByEditorHost } from './utils.js';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { TitleBandProperty } from '../../affine-shared/services/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* How a chip of the title band draws a property's value, and which editor a
|
|
4
|
+
* click opens — pure, render-free, so a host's own properties panel can show
|
|
5
|
+
* the very text a chip shows (`formatPropertyValue`, re-exported by
|
|
6
|
+
* `@labre/affine/host-panels`).
|
|
7
|
+
*
|
|
8
|
+
* The value is the JSON a data-view cell of `type` holds (ADR 0035 §9d), and
|
|
9
|
+
* the shapes below are the data-view presets' own: an option id for `select`,
|
|
10
|
+
* option ids for `multi-select`, epoch milliseconds for `date`, and a number
|
|
11
|
+
* formatted with the number preset's options, so a chip and a database cell
|
|
12
|
+
* read the same.
|
|
13
|
+
*
|
|
14
|
+
* ponytail: the band draws its chips and editors itself, per type, rather
|
|
15
|
+
* than mounting the data-view cell renderers, which need a `Cell` bound to a
|
|
16
|
+
* view and a data source the band does not have. The ceiling is a type the
|
|
17
|
+
* band does not know: it is drawn as plain text and opens no editor. The
|
|
18
|
+
* upgrade path is a hosted data source for the page's sheet once
|
|
19
|
+
* `HostedDataSourceBase` exists (ADR 0035 §7, issue B1).
|
|
20
|
+
*/
|
|
21
|
+
/** A select option as a data-view `select` / `multi-select` holds it. */
|
|
22
|
+
export interface ChipTag {
|
|
23
|
+
id: string;
|
|
24
|
+
label: string;
|
|
25
|
+
/** A CSS colour (a `var(--…)` from the tag palette). */
|
|
26
|
+
color?: string;
|
|
27
|
+
}
|
|
28
|
+
export type ChipValueView = {
|
|
29
|
+
kind: 'empty';
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'text';
|
|
32
|
+
text: string;
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'checkbox';
|
|
35
|
+
checked: boolean;
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'tags';
|
|
38
|
+
tags: ChipTag[];
|
|
39
|
+
};
|
|
40
|
+
/** The editor a click on a chip opens; `none` for a type the band cannot edit. */
|
|
41
|
+
export type ChipEditorKind = 'toggle' | 'select' | 'multi-select' | 'date' | 'number' | 'text' | 'none';
|
|
42
|
+
/**
|
|
43
|
+
* The number preset's formats (`data-view/…/number/utils/formatter.ts`),
|
|
44
|
+
* restated rather than imported: the preset formats with
|
|
45
|
+
* `navigator.language`, which ignores the host's locale and throws on a tag
|
|
46
|
+
* `Intl` refuses (`en-US@posix` in a headless Chromium), and a chip must
|
|
47
|
+
* neither. `title-band-number-parity.unit.spec.ts` (`affine/all`) holds the
|
|
48
|
+
* two tables together.
|
|
49
|
+
*/
|
|
50
|
+
export declare const CHIP_NUMBER_FORMATS: Readonly<Record<string, Intl.NumberFormatOptions>>;
|
|
51
|
+
/** The options of a select-like property, whatever its `data` holds. */
|
|
52
|
+
export declare function chipOptions(property: TitleBandProperty): ChipTag[];
|
|
53
|
+
/** Nullish, a blank string or an empty list. `false` is a value, not empty. */
|
|
54
|
+
export declare function isPropertyValueEmpty(property: TitleBandProperty): boolean;
|
|
55
|
+
/** What a chip draws for this property's value. Never throws. */
|
|
56
|
+
export declare function chipValueView(property: TitleBandProperty, locale: string): ChipValueView;
|
|
57
|
+
/**
|
|
58
|
+
* The value as one line of text, as a chip reads it: option labels joined by
|
|
59
|
+
* a comma, an empty value as `''`. A checkbox has no text either: it is a
|
|
60
|
+
* mark, not a word, so a host draws its own from `property.value`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function formatPropertyValue(property: TitleBandProperty, locale?: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* A number typed in a chip's editor: blanks dropped, a decimal comma read as
|
|
65
|
+
* a point. `NaN` for anything else, which the band writes as `null`.
|
|
66
|
+
*/
|
|
67
|
+
export declare function parseChipNumber(text: string): number;
|
|
68
|
+
/** The editor a click on this property's chip opens. */
|
|
69
|
+
export declare function chipEditorKind(property: TitleBandProperty): ChipEditorKind;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
const TEXT_TYPES = new Set([
|
|
2
|
+
'text',
|
|
3
|
+
'rich-text',
|
|
4
|
+
'title',
|
|
5
|
+
'url',
|
|
6
|
+
'link',
|
|
7
|
+
'email',
|
|
8
|
+
'phone',
|
|
9
|
+
]);
|
|
10
|
+
const DATE_TYPES = new Set(['date', 'created-time', 'modified-time']);
|
|
11
|
+
const currency = (code) => ({
|
|
12
|
+
style: 'currency',
|
|
13
|
+
currency: code,
|
|
14
|
+
currencyDisplay: 'symbol',
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* The number preset's formats (`data-view/…/number/utils/formatter.ts`),
|
|
18
|
+
* restated rather than imported: the preset formats with
|
|
19
|
+
* `navigator.language`, which ignores the host's locale and throws on a tag
|
|
20
|
+
* `Intl` refuses (`en-US@posix` in a headless Chromium), and a chip must
|
|
21
|
+
* neither. `title-band-number-parity.unit.spec.ts` (`affine/all`) holds the
|
|
22
|
+
* two tables together.
|
|
23
|
+
*/
|
|
24
|
+
export const CHIP_NUMBER_FORMATS = {
|
|
25
|
+
number: { style: 'decimal', useGrouping: false },
|
|
26
|
+
numberWithCommas: { style: 'decimal', useGrouping: true },
|
|
27
|
+
percent: { style: 'percent', useGrouping: false },
|
|
28
|
+
currencyINR: currency('INR'),
|
|
29
|
+
currencyYen: currency('JPY'),
|
|
30
|
+
currencyCNY: currency('CNY'),
|
|
31
|
+
currencyUSD: currency('USD'),
|
|
32
|
+
currencyEUR: currency('EUR'),
|
|
33
|
+
currencyGBP: currency('GBP'),
|
|
34
|
+
};
|
|
35
|
+
/** `Intl.NumberFormat`, or the bare number when `Intl` refuses the locale. */
|
|
36
|
+
function numberText(value, locale, options = {}) {
|
|
37
|
+
try {
|
|
38
|
+
return new Intl.NumberFormat(locale, options).format(value);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return String(value);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** The options of a select-like property, whatever its `data` holds. */
|
|
45
|
+
export function chipOptions(property) {
|
|
46
|
+
const options = property.data.options;
|
|
47
|
+
if (!Array.isArray(options))
|
|
48
|
+
return [];
|
|
49
|
+
const tags = [];
|
|
50
|
+
for (const option of options) {
|
|
51
|
+
if (option == null || typeof option !== 'object')
|
|
52
|
+
continue;
|
|
53
|
+
const { id, value, color } = option;
|
|
54
|
+
if (typeof id !== 'string')
|
|
55
|
+
continue;
|
|
56
|
+
tags.push({
|
|
57
|
+
id,
|
|
58
|
+
label: typeof value === 'string' ? value : '',
|
|
59
|
+
color: typeof color === 'string' ? color : undefined,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return tags;
|
|
63
|
+
}
|
|
64
|
+
/** Nullish, a blank string or an empty list. `false` is a value, not empty. */
|
|
65
|
+
export function isPropertyValueEmpty(property) {
|
|
66
|
+
const { value } = property;
|
|
67
|
+
if (value == null)
|
|
68
|
+
return true;
|
|
69
|
+
if (typeof value === 'string')
|
|
70
|
+
return value.trim() === '';
|
|
71
|
+
if (Array.isArray(value))
|
|
72
|
+
return value.length === 0;
|
|
73
|
+
if (typeof value === 'number')
|
|
74
|
+
return Number.isNaN(value);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
function formatDate(value, locale) {
|
|
78
|
+
const date = new Date(value);
|
|
79
|
+
if (Number.isNaN(date.getTime()))
|
|
80
|
+
return '';
|
|
81
|
+
try {
|
|
82
|
+
return new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(date);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return date.toISOString().slice(0, 10);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function formatNumberValue(property, value, locale) {
|
|
89
|
+
const { format, decimal } = property.data;
|
|
90
|
+
const options = {
|
|
91
|
+
...(typeof format === 'string' && Object.hasOwn(CHIP_NUMBER_FORMATS, format)
|
|
92
|
+
? CHIP_NUMBER_FORMATS[format]
|
|
93
|
+
: CHIP_NUMBER_FORMATS.number),
|
|
94
|
+
};
|
|
95
|
+
if (typeof decimal === 'number') {
|
|
96
|
+
options.minimumFractionDigits = decimal;
|
|
97
|
+
options.maximumFractionDigits = decimal;
|
|
98
|
+
}
|
|
99
|
+
return numberText(value, locale, options);
|
|
100
|
+
}
|
|
101
|
+
/** What a chip draws for this property's value. Never throws. */
|
|
102
|
+
export function chipValueView(property, locale) {
|
|
103
|
+
const { type, value } = property;
|
|
104
|
+
if (type === 'checkbox')
|
|
105
|
+
return { kind: 'checkbox', checked: value === true };
|
|
106
|
+
if (isPropertyValueEmpty(property))
|
|
107
|
+
return { kind: 'empty' };
|
|
108
|
+
if (type === 'select' || type === 'multi-select') {
|
|
109
|
+
const ids = Array.isArray(value) ? value : [value];
|
|
110
|
+
const options = chipOptions(property);
|
|
111
|
+
const tags = ids.flatMap(id => options.filter(option => option.id === id));
|
|
112
|
+
return tags.length > 0 ? { kind: 'tags', tags } : { kind: 'empty' };
|
|
113
|
+
}
|
|
114
|
+
if (DATE_TYPES.has(type) && typeof value === 'number') {
|
|
115
|
+
const text = formatDate(value, locale);
|
|
116
|
+
return text ? { kind: 'text', text } : { kind: 'empty' };
|
|
117
|
+
}
|
|
118
|
+
if (type === 'number' && typeof value === 'number') {
|
|
119
|
+
return { kind: 'text', text: formatNumberValue(property, value, locale) };
|
|
120
|
+
}
|
|
121
|
+
if (type === 'progress' && typeof value === 'number') {
|
|
122
|
+
return {
|
|
123
|
+
kind: 'text',
|
|
124
|
+
text: numberText(value / 100, locale, { style: 'percent' }),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (typeof value === 'string')
|
|
128
|
+
return { kind: 'text', text: value };
|
|
129
|
+
if (typeof value === 'number') {
|
|
130
|
+
return { kind: 'text', text: numberText(value, locale) };
|
|
131
|
+
}
|
|
132
|
+
if (Array.isArray(value)) {
|
|
133
|
+
const text = value.filter(item => typeof item === 'string').join(', ');
|
|
134
|
+
return text ? { kind: 'text', text } : { kind: 'empty' };
|
|
135
|
+
}
|
|
136
|
+
return { kind: 'empty' };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The value as one line of text, as a chip reads it: option labels joined by
|
|
140
|
+
* a comma, an empty value as `''`. A checkbox has no text either: it is a
|
|
141
|
+
* mark, not a word, so a host draws its own from `property.value`.
|
|
142
|
+
*/
|
|
143
|
+
export function formatPropertyValue(property, locale = 'en-US') {
|
|
144
|
+
const view = chipValueView(property, locale);
|
|
145
|
+
switch (view.kind) {
|
|
146
|
+
case 'text':
|
|
147
|
+
return view.text;
|
|
148
|
+
case 'tags':
|
|
149
|
+
return view.tags.map(tag => tag.label).join(', ');
|
|
150
|
+
default:
|
|
151
|
+
return '';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* A number typed in a chip's editor: blanks dropped, a decimal comma read as
|
|
156
|
+
* a point. `NaN` for anything else, which the band writes as `null`.
|
|
157
|
+
*/
|
|
158
|
+
export function parseChipNumber(text) {
|
|
159
|
+
const compact = text.replace(/\s/g, '');
|
|
160
|
+
if (compact === '')
|
|
161
|
+
return NaN;
|
|
162
|
+
return Number(compact.replace(',', '.'));
|
|
163
|
+
}
|
|
164
|
+
/** The editor a click on this property's chip opens. */
|
|
165
|
+
export function chipEditorKind(property) {
|
|
166
|
+
if (property.readonly)
|
|
167
|
+
return 'none';
|
|
168
|
+
const { type } = property;
|
|
169
|
+
if (type === 'checkbox')
|
|
170
|
+
return 'toggle';
|
|
171
|
+
if (type === 'select')
|
|
172
|
+
return 'select';
|
|
173
|
+
if (type === 'multi-select')
|
|
174
|
+
return 'multi-select';
|
|
175
|
+
if (type === 'date')
|
|
176
|
+
return 'date';
|
|
177
|
+
if (type === 'number' || type === 'progress')
|
|
178
|
+
return 'number';
|
|
179
|
+
if (TEXT_TYPES.has(type))
|
|
180
|
+
return 'text';
|
|
181
|
+
return 'none';
|
|
182
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type BlockStdScope, ShadowlessElement } from '../../std/index.js';
|
|
2
|
+
import { nothing, type TemplateResult } from 'lit';
|
|
3
|
+
export declare const LABRE_TITLE_BAND = "labre-title-band";
|
|
4
|
+
declare const TitleBand_base: typeof ShadowlessElement & import("../../global/utils/types.js").Constructor<import("../../global/lit/with-disposable.js").DisposableClass>;
|
|
5
|
+
/**
|
|
6
|
+
* The band of property chips under the page title and at the head of the
|
|
7
|
+
* page's note on the canvas (ADR 0035 §11, REQ-PR-1 and PR-7).
|
|
8
|
+
*
|
|
9
|
+
* One component for both views: `<doc-title>` mounts it under the title
|
|
10
|
+
* (`surface="page"`), `edgeless-page-block-title` at the head of the page's
|
|
11
|
+
* note (`surface="canvas"`). On the canvas it sits inside the note's own DOM,
|
|
12
|
+
* so it follows the zoom like the rest of the note (REQ-PR-7), and it is
|
|
13
|
+
* interactive when the note is, i.e. once the note is being edited.
|
|
14
|
+
*
|
|
15
|
+
* Fed by `TitleBandProvider` alone (§9d): no provider, nothing at all. The
|
|
16
|
+
* chips are the sheet's visible properties in the host's order, an empty one
|
|
17
|
+
* included (REQ-PR-1); a click opens the editor of the property's data-view
|
|
18
|
+
* type and hands the value back through `set`, showing the chip as pending
|
|
19
|
+
* until the host settles it. The band never writes to the document and never
|
|
20
|
+
* applies a value. The "+" is drawn when the host passes `add`; the panel
|
|
21
|
+
* button when a `PropertiesPanelProvider` is registered; the "Not shown in
|
|
22
|
+
* this document" row only on the canvas (the page view's row is the host's).
|
|
23
|
+
*/
|
|
24
|
+
export declare class TitleBand extends TitleBand_base {
|
|
25
|
+
static styles: import("lit").CSSResult;
|
|
26
|
+
private _sources;
|
|
27
|
+
/** The editor menu open now, put away with the band. */
|
|
28
|
+
private _menu;
|
|
29
|
+
private _pop;
|
|
30
|
+
/** Collapse state when the host does not remember one. */
|
|
31
|
+
private readonly _localCollapsed$;
|
|
32
|
+
/** Property ids whose `set` has not settled yet. */
|
|
33
|
+
private readonly _pending$;
|
|
34
|
+
/** Read inside `render` it subscribes, through `SignalWatcher`. */
|
|
35
|
+
private get _readonly();
|
|
36
|
+
private get _locale();
|
|
37
|
+
private _t;
|
|
38
|
+
private _getSources;
|
|
39
|
+
private _isCollapsed;
|
|
40
|
+
private _setCollapsed;
|
|
41
|
+
private _write;
|
|
42
|
+
private readonly _onChip;
|
|
43
|
+
private _tagLabel;
|
|
44
|
+
private _clearItem;
|
|
45
|
+
private _popSelect;
|
|
46
|
+
private _popMultiSelect;
|
|
47
|
+
private _popDate;
|
|
48
|
+
private _popInput;
|
|
49
|
+
private readonly _openPanel;
|
|
50
|
+
private _renderValue;
|
|
51
|
+
private _renderChip;
|
|
52
|
+
private _renderTools;
|
|
53
|
+
private _renderSheet;
|
|
54
|
+
private _renderNotShown;
|
|
55
|
+
connectedCallback(): void;
|
|
56
|
+
render(): TemplateResult<1> | typeof nothing;
|
|
57
|
+
accessor std: BlockStdScope | null;
|
|
58
|
+
accessor docId: string;
|
|
59
|
+
/** Where the band is drawn; only the canvas draws the "Not shown" row. */
|
|
60
|
+
accessor surface: 'page' | 'canvas';
|
|
61
|
+
}
|
|
62
|
+
export {};
|