@webx-ui/module-blocks 0.1.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 +21 -0
- package/README.md +62 -0
- package/dist/BlockCard.d.ts +11 -0
- package/dist/BlockChecks.d.ts +19 -0
- package/dist/BlockCreateDialog.d.ts +2 -0
- package/dist/BlockEditorPage.d.ts +86 -0
- package/dist/BlockHistory.d.ts +15 -0
- package/dist/BlockPicker.d.ts +27 -0
- package/dist/BlockStage.d.ts +21 -0
- package/dist/BlockThumb.d.ts +22 -0
- package/dist/BlocksField.d.ts +78 -0
- package/dist/BlocksPage.d.ts +12 -0
- package/dist/BlocksPreview.d.ts +34 -0
- package/dist/BlocksTree.d.ts +37 -0
- package/dist/api.d.ts +20 -0
- package/dist/content.d.ts +43 -0
- package/dist/frame.d.ts +43 -0
- package/dist/i18n.d.ts +2 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +2461 -0
- package/dist/index.js.map +1 -0
- package/dist/lint.d.ts +23 -0
- package/dist/messages.d.ts +7 -0
- package/dist/module.d.ts +13 -0
- package/dist/preview.d.ts +25 -0
- package/dist/schema.d.ts +13 -0
- package/dist/style.css +1 -0
- package/dist/types.d.ts +126 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 webx-ui
|
|
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
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @webx-ui/module-blocks
|
|
2
|
+
|
|
3
|
+
The block constructor as a section of the [WebX UI](https://github.com/webx-ui/webx-ui) admin
|
|
4
|
+
panel: the screen where a block type is made entirely — its fields, its Blade template, its
|
|
5
|
+
styles and its script — and `wx-blocks`, the field that builds an entity's content out of such
|
|
6
|
+
blocks.
|
|
7
|
+
|
|
8
|
+
The other half is `webx-ui/module-blocks` on the server, which stores the types, renders them and
|
|
9
|
+
serves the preview. Neither is useful alone.
|
|
10
|
+
|
|
11
|
+
**Access to the section is access to deployment.** A block type is Blade, and Blade is PHP.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @webx-ui/module-blocks
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createAdmin } from '@webx-ui/module-admin'
|
|
21
|
+
import { blocks } from '@webx-ui/module-blocks'
|
|
22
|
+
import '@webx-ui/module-blocks/style.css'
|
|
23
|
+
|
|
24
|
+
createAdmin({
|
|
25
|
+
modules: [blocks()],
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The section appears at the top level once the Composer half is installed and migrated.
|
|
30
|
+
Permissions: `blocks.view`, `blocks.manage`. With `webx-blocks.editing` off on the server the
|
|
31
|
+
section is read-only whatever the permission says.
|
|
32
|
+
|
|
33
|
+
## What it brings
|
|
34
|
+
|
|
35
|
+
- `/blocks` — every type as a card with a live thumbnail drawn on its sample values, grouped the
|
|
36
|
+
way the picker groups them.
|
|
37
|
+
- `/blocks/:id` — the editor: template, styles, script, fields and settings on the left; the
|
|
38
|
+
block drawn on its sample at a desktop, tablet or phone width, the sample's form built from the
|
|
39
|
+
schema being edited, and where the type stands on the right. The loop is closed: edit the
|
|
40
|
+
schema and the form rebuilds, edit the values and the stage redraws, edit the template and so
|
|
41
|
+
does it. Under the editor, what was noticed — a selector outside the block's prefix, a bare
|
|
42
|
+
element selector, a media query, a missing `data-wx-block`, a variable the schema does not
|
|
43
|
+
declare — and what was not.
|
|
44
|
+
- **Publish** — a separate step, refused with the line when the template fails on the sample or
|
|
45
|
+
on any page the block already stands on. The history lists a version per save; restoring one
|
|
46
|
+
makes it the draft.
|
|
47
|
+
- `wx-blocks` — a field type any screen can put on an entity. Nothing selected: the tree of
|
|
48
|
+
blocks and the whole page in preview. A block selected: the tree, its fields, the page as a
|
|
49
|
+
phone at one to one. The preview is the entity's own `/_preview/…` address, handed in by the
|
|
50
|
+
hosting screen:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { provideBlocksPreview } from '@webx-ui/module-blocks'
|
|
54
|
+
|
|
55
|
+
provideBlocksPreview({ url: previewUrl, reload: reloadCounter })
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Without it the field is a tree with a form, which is all a screen outside the panel can be.
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BlockType } from './types';
|
|
2
|
+
/** One type in the list: its picture, its name, where it stands and which versions it has. */
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
block: BlockType;
|
|
5
|
+
};
|
|
6
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
7
|
+
open: (block: BlockType) => any;
|
|
8
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
9
|
+
onOpen?: ((block: BlockType) => any) | undefined;
|
|
10
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLButtonElement>;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Lint } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* The strip under an editor: what was noticed, and what was not — a green line for every
|
|
4
|
+
* check that passed, so that silence reads as "checked" rather than "not looked at".
|
|
5
|
+
*/
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
/** Which editor this strip sits under. */
|
|
8
|
+
file: 'template' | 'styles' | 'script' | 'schema';
|
|
9
|
+
lints: Lint[];
|
|
10
|
+
slug: string;
|
|
11
|
+
/** A refusal from publishing, shown first and in red. */
|
|
12
|
+
error?: {
|
|
13
|
+
message: string;
|
|
14
|
+
line: number | null;
|
|
15
|
+
where?: string | null;
|
|
16
|
+
} | null;
|
|
17
|
+
};
|
|
18
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor of one type: two columns. On the left the four files and the settings, on the
|
|
3
|
+
* right the block drawn on its sample, the sample's form built from the schema being edited,
|
|
4
|
+
* and where the type stands. The loop is closed — edit the schema and the form rebuilds,
|
|
5
|
+
* edit the values and the stage redraws, edit the template or the styles and so does it.
|
|
6
|
+
*/
|
|
7
|
+
type __VLS_Props = {
|
|
8
|
+
base?: string;
|
|
9
|
+
};
|
|
10
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
11
|
+
base: string;
|
|
12
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
13
|
+
templateEditor: import('vue').CreateComponentPublicInstanceWithMixins<Readonly<{
|
|
14
|
+
modelValue?: string;
|
|
15
|
+
} & import('@webx-ui/core').CodeEditorProps> & Readonly<{
|
|
16
|
+
onChange?: ((value: string) => any) | undefined;
|
|
17
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
18
|
+
onBlur?: (() => any) | undefined;
|
|
19
|
+
onFocus?: (() => any) | undefined;
|
|
20
|
+
onLint?: ((diagnostics: import('@webx-ui/core').CodeEditorDiagnostic[]) => any) | undefined;
|
|
21
|
+
}>, {
|
|
22
|
+
view: import('vue').ShallowRef<import('@codemirror/view').EditorView | null, import('@codemirror/view').EditorView | null>;
|
|
23
|
+
focus: () => void | undefined;
|
|
24
|
+
format: () => boolean;
|
|
25
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
26
|
+
change: (value: string) => any;
|
|
27
|
+
"update:modelValue": (value: string) => any;
|
|
28
|
+
blur: () => any;
|
|
29
|
+
focus: () => any;
|
|
30
|
+
lint: (diagnostics: import('@webx-ui/core').CodeEditorDiagnostic[]) => any;
|
|
31
|
+
}, import('vue').PublicProps, {
|
|
32
|
+
size: import('@webx-ui/core').ControlSize;
|
|
33
|
+
disabled: boolean;
|
|
34
|
+
placeholder: string;
|
|
35
|
+
ariaLabel: string;
|
|
36
|
+
status: import('@webx-ui/core').ControlStatus;
|
|
37
|
+
id: string;
|
|
38
|
+
language: import('@webx-ui/core').CodeEditorLanguage;
|
|
39
|
+
readonly: boolean;
|
|
40
|
+
minHeight: string;
|
|
41
|
+
maxHeight: string;
|
|
42
|
+
lineNumbers: boolean;
|
|
43
|
+
lineWrapping: boolean;
|
|
44
|
+
tabSize: number;
|
|
45
|
+
lint: boolean;
|
|
46
|
+
extensions: import('@codemirror/state').Extension[];
|
|
47
|
+
}, false, {}, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, {
|
|
48
|
+
host: HTMLDivElement;
|
|
49
|
+
}, any, import('vue').ComponentProvideOptions, {
|
|
50
|
+
P: {};
|
|
51
|
+
B: {};
|
|
52
|
+
D: {};
|
|
53
|
+
C: {};
|
|
54
|
+
M: {};
|
|
55
|
+
Defaults: {};
|
|
56
|
+
}, Readonly<{
|
|
57
|
+
modelValue?: string;
|
|
58
|
+
} & import('@webx-ui/core').CodeEditorProps> & Readonly<{
|
|
59
|
+
onChange?: ((value: string) => any) | undefined;
|
|
60
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
61
|
+
onBlur?: (() => any) | undefined;
|
|
62
|
+
onFocus?: (() => any) | undefined;
|
|
63
|
+
onLint?: ((diagnostics: import('@webx-ui/core').CodeEditorDiagnostic[]) => any) | undefined;
|
|
64
|
+
}>, {
|
|
65
|
+
view: import('vue').ShallowRef<import('@codemirror/view').EditorView | null, import('@codemirror/view').EditorView | null>;
|
|
66
|
+
focus: () => void | undefined;
|
|
67
|
+
format: () => boolean;
|
|
68
|
+
}, {}, {}, {}, {
|
|
69
|
+
size: import('@webx-ui/core').ControlSize;
|
|
70
|
+
disabled: boolean;
|
|
71
|
+
placeholder: string;
|
|
72
|
+
ariaLabel: string;
|
|
73
|
+
status: import('@webx-ui/core').ControlStatus;
|
|
74
|
+
id: string;
|
|
75
|
+
language: import('@webx-ui/core').CodeEditorLanguage;
|
|
76
|
+
readonly: boolean;
|
|
77
|
+
minHeight: string;
|
|
78
|
+
maxHeight: string;
|
|
79
|
+
lineNumbers: boolean;
|
|
80
|
+
lineWrapping: boolean;
|
|
81
|
+
tabSize: number;
|
|
82
|
+
lint: boolean;
|
|
83
|
+
extensions: import('@codemirror/state').Extension[];
|
|
84
|
+
}> | null;
|
|
85
|
+
}, HTMLDivElement>;
|
|
86
|
+
export default _default;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BlockType } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* A version per save, newest first, each with who and why. Restoring makes the old version a
|
|
4
|
+
* new draft — publishing it is the same separate step it always is.
|
|
5
|
+
*/
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
block: BlockType;
|
|
8
|
+
canManage: boolean;
|
|
9
|
+
};
|
|
10
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
11
|
+
restored: (block: BlockType) => any;
|
|
12
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
13
|
+
onRestored?: ((block: BlockType) => any) | undefined;
|
|
14
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BlockNode, BlockType } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* "Add a block": the types as cards with their pictures, because a name says nothing about
|
|
4
|
+
* whether "Section" is a full-width band or a container with columns. Only what may go
|
|
5
|
+
* here is offered; what is exhausted is shown greyed with the reason rather than hidden,
|
|
6
|
+
* or the editor goes looking for a block that vanished.
|
|
7
|
+
*/
|
|
8
|
+
type __VLS_Props = {
|
|
9
|
+
catalog: BlockType[];
|
|
10
|
+
/** The container a block is being added to, or null at the page level. */
|
|
11
|
+
parent?: BlockType | null;
|
|
12
|
+
/** What the container's field allows, when the field narrows it further. */
|
|
13
|
+
allow?: string[] | null;
|
|
14
|
+
/** The whole tree, for the per-page limits. */
|
|
15
|
+
tree?: BlockNode[];
|
|
16
|
+
groups?: string[];
|
|
17
|
+
/** Where a person with the right can go and make a type. */
|
|
18
|
+
blocksPath?: string | null;
|
|
19
|
+
};
|
|
20
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
21
|
+
parent: BlockType | null;
|
|
22
|
+
allow: string[] | null;
|
|
23
|
+
groups: string[];
|
|
24
|
+
tree: BlockNode[];
|
|
25
|
+
blocksPath: string | null;
|
|
26
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
27
|
+
export default _default;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The block on its own, at a chosen width: a desktop scaled down to fit the column, a tablet
|
|
3
|
+
* or a phone at one to one. The document is the block, its styles, its script and the
|
|
4
|
+
* runtime, and nothing of the panel — an iframe, so the block's CSS stays the block's.
|
|
5
|
+
*/
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
html: string;
|
|
8
|
+
styles: string;
|
|
9
|
+
script?: string | null;
|
|
10
|
+
runtime?: string | null;
|
|
11
|
+
loading?: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
14
|
+
script: string | null;
|
|
15
|
+
loading: boolean;
|
|
16
|
+
runtime: string | null;
|
|
17
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
18
|
+
box: HTMLDivElement;
|
|
19
|
+
frame: HTMLIFrameElement;
|
|
20
|
+
}, HTMLDivElement>;
|
|
21
|
+
export default _default;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BlockThumbnail } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* A block drawn small: the sample render in an iframe, scaled down to the width it is given.
|
|
4
|
+
*
|
|
5
|
+
* An iframe rather than inline markup because a block's styles are a block's business — a
|
|
6
|
+
* stray `h2 { }` in one type would otherwise restyle the whole list of cards — and because
|
|
7
|
+
* the thumbnail is a picture of the site, not a part of the panel.
|
|
8
|
+
*/
|
|
9
|
+
type __VLS_Props = {
|
|
10
|
+
thumbnail: BlockThumbnail | null;
|
|
11
|
+
/** The width the block is drawn at, before scaling — a desktop. */
|
|
12
|
+
width?: number;
|
|
13
|
+
/** How tall the picture is, in the panel's pixels. */
|
|
14
|
+
height?: number;
|
|
15
|
+
};
|
|
16
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
20
|
+
box: HTMLDivElement;
|
|
21
|
+
}, HTMLDivElement>;
|
|
22
|
+
export default _default;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { RenderContext, ScreenNode } from '@webx-ui/schema';
|
|
2
|
+
import { BlockNode, BlockType } from './types';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
/** Handed over by the renderer: the node being drawn and the way down. Unused here. */
|
|
5
|
+
node?: ScreenNode;
|
|
6
|
+
context?: RenderContext;
|
|
7
|
+
name?: string;
|
|
8
|
+
localized?: boolean;
|
|
9
|
+
/** Narrows what may be added at the top level, beyond what the types themselves allow. */
|
|
10
|
+
allow?: string[] | null;
|
|
11
|
+
/** How many blocks the top level may hold. */
|
|
12
|
+
max?: number | null;
|
|
13
|
+
/**
|
|
14
|
+
* The published types with their schemas. Fetched from the panel when absent — handed
|
|
15
|
+
* in for a screen outside one, a demo or a test.
|
|
16
|
+
*/
|
|
17
|
+
catalog?: BlockType[] | null;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
/** Where the section lives, for the picker's "make one" link. */
|
|
20
|
+
blocksPath?: string;
|
|
21
|
+
};
|
|
22
|
+
type __VLS_PublicProps = {
|
|
23
|
+
modelValue?: BlockNode[];
|
|
24
|
+
} & __VLS_Props;
|
|
25
|
+
declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
26
|
+
"update:modelValue": (value: BlockNode[]) => any;
|
|
27
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
28
|
+
"onUpdate:modelValue"?: ((value: BlockNode[]) => any) | undefined;
|
|
29
|
+
}>, {
|
|
30
|
+
name: string;
|
|
31
|
+
disabled: boolean;
|
|
32
|
+
node: ScreenNode;
|
|
33
|
+
allow: string[] | null;
|
|
34
|
+
localized: boolean;
|
|
35
|
+
max: number | null;
|
|
36
|
+
context: RenderContext;
|
|
37
|
+
catalog: BlockType[] | null;
|
|
38
|
+
blocksPath: string;
|
|
39
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
40
|
+
previewEl: import('vue').CreateComponentPublicInstanceWithMixins<Readonly<{
|
|
41
|
+
url: string | null;
|
|
42
|
+
selected?: string | null;
|
|
43
|
+
mode?: "wide" | "phone";
|
|
44
|
+
reload?: number;
|
|
45
|
+
}> & Readonly<{}>, {
|
|
46
|
+
replace: (key: string, html: string) => boolean;
|
|
47
|
+
refresh: () => void;
|
|
48
|
+
open: () => boolean;
|
|
49
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, import('vue').PublicProps, {
|
|
50
|
+
mode: "wide" | "phone";
|
|
51
|
+
selected: string | null;
|
|
52
|
+
reload: number;
|
|
53
|
+
}, false, {}, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, {
|
|
54
|
+
box: HTMLDivElement;
|
|
55
|
+
frame: HTMLIFrameElement;
|
|
56
|
+
}, HTMLDivElement, import('vue').ComponentProvideOptions, {
|
|
57
|
+
P: {};
|
|
58
|
+
B: {};
|
|
59
|
+
D: {};
|
|
60
|
+
C: {};
|
|
61
|
+
M: {};
|
|
62
|
+
Defaults: {};
|
|
63
|
+
}, Readonly<{
|
|
64
|
+
url: string | null;
|
|
65
|
+
selected?: string | null;
|
|
66
|
+
mode?: "wide" | "phone";
|
|
67
|
+
reload?: number;
|
|
68
|
+
}> & Readonly<{}>, {
|
|
69
|
+
replace: (key: string, html: string) => boolean;
|
|
70
|
+
refresh: () => void;
|
|
71
|
+
open: () => boolean;
|
|
72
|
+
}, {}, {}, {}, {
|
|
73
|
+
mode: "wide" | "phone";
|
|
74
|
+
selected: string | null;
|
|
75
|
+
reload: number;
|
|
76
|
+
}> | null;
|
|
77
|
+
}, any>;
|
|
78
|
+
export default _default;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The section: every type as a card with a live thumbnail, grouped the way the picker groups
|
|
3
|
+
* them. Cards rather than a table because a block is recognised by its picture — "Section"
|
|
4
|
+
* says nothing about whether it is a full-width band or a container with columns.
|
|
5
|
+
*/
|
|
6
|
+
type __VLS_Props = {
|
|
7
|
+
base?: string;
|
|
8
|
+
};
|
|
9
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
10
|
+
base: string;
|
|
11
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page in an iframe, the way it will be: the same handler, the same view, the draft
|
|
3
|
+
* laid over the columns. Two shapes — the whole page wide while nothing is selected, a
|
|
4
|
+
* phone at one to one while a block is being edited — and a full-screen mode for looking
|
|
5
|
+
* at the desktop and the tablet, because a desktop in a 420 px column is a picture nobody
|
|
6
|
+
* can read.
|
|
7
|
+
*
|
|
8
|
+
* The panel and the site are one application on one domain, so the frame's document is
|
|
9
|
+
* reachable: selection is a class, a change is a swap between the marker comments.
|
|
10
|
+
*/
|
|
11
|
+
type __VLS_Props = {
|
|
12
|
+
url: string | null;
|
|
13
|
+
selected?: string | null;
|
|
14
|
+
/** `wide` when the page is being looked at, `phone` while a block is being edited. */
|
|
15
|
+
mode?: 'wide' | 'phone';
|
|
16
|
+
/** Bumped by the host to reload the page. */
|
|
17
|
+
reload?: number;
|
|
18
|
+
};
|
|
19
|
+
/** Swap one block's markup in place; false when the page has to be reloaded instead. */
|
|
20
|
+
declare function replace(key: string, html: string): boolean;
|
|
21
|
+
declare function refresh(): void;
|
|
22
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {
|
|
23
|
+
replace: typeof replace;
|
|
24
|
+
refresh: typeof refresh;
|
|
25
|
+
open: () => boolean;
|
|
26
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
27
|
+
mode: "wide" | "phone";
|
|
28
|
+
selected: string | null;
|
|
29
|
+
reload: number;
|
|
30
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
31
|
+
box: HTMLDivElement;
|
|
32
|
+
frame: HTMLIFrameElement;
|
|
33
|
+
}, HTMLDivElement>;
|
|
34
|
+
export default _default;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ScreenNode } from '@webx-ui/schema';
|
|
2
|
+
import { BlockNode, BlockType } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* One level of the tree, and itself again for every container. Rows reorder among their
|
|
5
|
+
* siblings by drag; a container shows its fields' lists underneath, each with its own
|
|
6
|
+
* "add inside". Moving a block between containers is not a drag — a duplicate and a remove
|
|
7
|
+
* do it, and a drag across allow-rules would need a judge at every drop.
|
|
8
|
+
*/
|
|
9
|
+
type __VLS_Props = {
|
|
10
|
+
nodes: BlockNode[];
|
|
11
|
+
catalog: BlockType[];
|
|
12
|
+
parentKey?: string | null;
|
|
13
|
+
field?: string | null;
|
|
14
|
+
selected?: string | null;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
depth?: number;
|
|
17
|
+
};
|
|
18
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
19
|
+
select: (key: string) => any;
|
|
20
|
+
add: (parentKey: string | null, field: string | null, node: ScreenNode | null) => any;
|
|
21
|
+
remove: (key: string) => any;
|
|
22
|
+
duplicate: (key: string) => any;
|
|
23
|
+
reorder: (parentKey: string | null, field: string | null, list: BlockNode[]) => any;
|
|
24
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
25
|
+
onSelect?: ((key: string) => any) | undefined;
|
|
26
|
+
onAdd?: ((parentKey: string | null, field: string | null, node: ScreenNode | null) => any) | undefined;
|
|
27
|
+
onRemove?: ((key: string) => any) | undefined;
|
|
28
|
+
onDuplicate?: ((key: string) => any) | undefined;
|
|
29
|
+
onReorder?: ((parentKey: string | null, field: string | null, list: BlockNode[]) => any) | undefined;
|
|
30
|
+
}>, {
|
|
31
|
+
disabled: boolean;
|
|
32
|
+
field: string | null;
|
|
33
|
+
selected: string | null;
|
|
34
|
+
parentKey: string | null;
|
|
35
|
+
depth: number;
|
|
36
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
37
|
+
export default _default;
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AdminContext } from '@webx-ui/module-admin';
|
|
2
|
+
import { BlockInput, BlockType, BlockUsage, BlockVersion, BlockVersionMeta, RenderInput, RenderResult } from './types';
|
|
3
|
+
export interface BlocksApi {
|
|
4
|
+
/** Every type, without content: the section's list. */
|
|
5
|
+
list(): Promise<BlockType[]>;
|
|
6
|
+
/** The published types with their content: what the constructor and the picker read. */
|
|
7
|
+
catalog(): Promise<BlockType[]>;
|
|
8
|
+
get(id: number): Promise<BlockType>;
|
|
9
|
+
create(input: BlockInput): Promise<BlockType>;
|
|
10
|
+
update(id: number, input: BlockInput): Promise<BlockType>;
|
|
11
|
+
remove(id: number): Promise<void>;
|
|
12
|
+
publish(id: number): Promise<BlockType>;
|
|
13
|
+
render(id: number, input?: RenderInput): Promise<RenderResult>;
|
|
14
|
+
usage(id: number): Promise<BlockUsage[]>;
|
|
15
|
+
versions(id: number): Promise<BlockVersionMeta[]>;
|
|
16
|
+
version(id: number, number: number): Promise<BlockVersion>;
|
|
17
|
+
restore(id: number, number: number): Promise<BlockType>;
|
|
18
|
+
}
|
|
19
|
+
/** Everything under `/blocks`, below the panel's API path. */
|
|
20
|
+
export declare function createBlocksApi(admin: AdminContext): BlocksApi;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ScreenNode } from '@webx-ui/schema';
|
|
2
|
+
import { BlockNode } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Walking and rewriting a tree of blocks.
|
|
5
|
+
*
|
|
6
|
+
* A node is `{ key, type, values }`, and a value that is itself a list of such nodes is a
|
|
7
|
+
* nested constructor — recognised by shape rather than by schema, so a tree can be walked
|
|
8
|
+
* for a type nobody can look up any more. Every rewrite returns a new tree and leaves the
|
|
9
|
+
* one it was given alone: the field's model is replaced, never mutated, which is what keeps
|
|
10
|
+
* the renderer's `update` and the preview's swap in step.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isNode(value: unknown): value is BlockNode;
|
|
13
|
+
/** A list of nodes — a nested constructor's value — as opposed to a list of anything else. */
|
|
14
|
+
export declare function isNodeList(value: unknown): value is BlockNode[];
|
|
15
|
+
/** An id that survives a drag: random, not positional. */
|
|
16
|
+
export declare function newKey(): string;
|
|
17
|
+
export declare function makeNode(type: string, values?: Record<string, unknown>): BlockNode;
|
|
18
|
+
/** Where a node sits: the list it is in, its index there, and the field of the parent. */
|
|
19
|
+
export interface Located {
|
|
20
|
+
node: BlockNode;
|
|
21
|
+
parent: BlockNode | null;
|
|
22
|
+
field: string | null;
|
|
23
|
+
list: BlockNode[];
|
|
24
|
+
index: number;
|
|
25
|
+
}
|
|
26
|
+
export declare function locate(tree: BlockNode[], key: string): Located | null;
|
|
27
|
+
/** Every node, parents before children. */
|
|
28
|
+
export declare function walk(tree: BlockNode[], visit: (node: BlockNode, depth: number, parent: BlockNode | null) => void, depth?: number, parent?: BlockNode | null): void;
|
|
29
|
+
/** How many instances of a type the tree holds, nested ones included. */
|
|
30
|
+
export declare function countType(tree: BlockNode[], slug: string): number;
|
|
31
|
+
/** The distinct types a tree uses, in first-seen order. */
|
|
32
|
+
export declare function typesIn(tree: BlockNode[]): string[];
|
|
33
|
+
/** The `wx-blocks` nodes of a schema, through cards and tabs. */
|
|
34
|
+
export declare function nestedFields(schema: ScreenNode[]): ScreenNode[];
|
|
35
|
+
/** A copy with fresh keys all the way down — what a duplicate is. */
|
|
36
|
+
export declare function cloneNode(node: BlockNode): BlockNode;
|
|
37
|
+
export declare function insertNode(tree: BlockNode[], parentKey: string | null, field: string | null, index: number, node: BlockNode): BlockNode[];
|
|
38
|
+
export declare function removeNode(tree: BlockNode[], key: string): BlockNode[];
|
|
39
|
+
export declare function updateValues(tree: BlockNode[], key: string, values: Record<string, unknown>): BlockNode[];
|
|
40
|
+
/** The list under one parent replaced wholesale — what a sortable list hands back. */
|
|
41
|
+
export declare function replaceList(tree: BlockNode[], parentKey: string | null, field: string | null, list: BlockNode[]): BlockNode[];
|
|
42
|
+
/** Values are JSON — that is the contract with the server — so a JSON round trip is a clone. */
|
|
43
|
+
export declare function clone<T>(value: T): T;
|
package/dist/frame.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preview is a page of the site in an iframe, and the panel's hold on it is a pair of
|
|
3
|
+
* comments around every block — `<!--wx:key-->…<!--/wx:key-->` — printed by the renderer in
|
|
4
|
+
* preview mode. Finding a block is walking the comments; replacing one is swapping what sits
|
|
5
|
+
* between the pair; highlighting one is a class on the first element after the opener.
|
|
6
|
+
*
|
|
7
|
+
* The panel and the site are one application on one domain, so the frame's document is
|
|
8
|
+
* reachable and nothing has to be injected into the site's markup beyond one stylesheet.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SELECTED_CLASS = "wx-preview-selected";
|
|
11
|
+
export interface MarkerRange {
|
|
12
|
+
start: Comment;
|
|
13
|
+
end: Comment;
|
|
14
|
+
}
|
|
15
|
+
/** The pair of markers around a block, or null when the page does not hold it. */
|
|
16
|
+
export declare function findRange(root: Node, key: string): MarkerRange | null;
|
|
17
|
+
/**
|
|
18
|
+
* Swap the block's markup — markers included, since the new HTML carries its own — and let
|
|
19
|
+
* the runtime mount whatever script the new markup needs. False when the key is not on the
|
|
20
|
+
* page, which means the page has to be reloaded rather than patched.
|
|
21
|
+
*/
|
|
22
|
+
export declare function replaceBlock(doc: Document, key: string, html: string): boolean;
|
|
23
|
+
/** The first element a block prints — where a highlight goes. */
|
|
24
|
+
export declare function blockElement(root: Node, key: string): Element | null;
|
|
25
|
+
/** Mark one block as the selected one, and nothing else. Null clears the selection. */
|
|
26
|
+
export declare function highlightBlock(doc: Document, key: string | null): Element | null;
|
|
27
|
+
export interface StageInput {
|
|
28
|
+
html: string;
|
|
29
|
+
styles: string;
|
|
30
|
+
/** The script wrapped for the runtime, or nothing. */
|
|
31
|
+
script?: string | null;
|
|
32
|
+
/** Where the runtime is served; skipped when there is no script to run. */
|
|
33
|
+
runtime?: string | null;
|
|
34
|
+
/** Resolves relative addresses the block prints — images, links — against the site. */
|
|
35
|
+
base?: string | null;
|
|
36
|
+
/** Styles the site's layout would give the page: fonts, the ground colour. */
|
|
37
|
+
frame?: string | null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A whole document for the editor's stage: the block on its own, with its styles, its script
|
|
41
|
+
* and the runtime, and nothing of the panel. `srcdoc` takes it.
|
|
42
|
+
*/
|
|
43
|
+
export declare function stageDocument(input: StageInput): string;
|
package/dist/i18n.d.ts
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { blocks, type BlocksOptions } from './module';
|
|
2
|
+
export { createBlocksApi, type BlocksApi } from './api';
|
|
3
|
+
export { blocksMessages } from './messages';
|
|
4
|
+
export { blocksPreviewKey, provideBlocksPreview, useBlocksPreview, type BlocksPreview, } from './preview';
|
|
5
|
+
export { cloneNode, countType, insertNode, isNode, isNodeList, locate, makeNode, newKey, removeNode, replaceList, typesIn, updateValues, walk, type Located, } from './content';
|
|
6
|
+
export { lintBlock, undeclared } from './lint';
|
|
7
|
+
export { findRange, highlightBlock, replaceBlock, stageDocument } from './frame';
|
|
8
|
+
export { formSchema } from './schema';
|
|
9
|
+
export { default as WxBlocks } from './BlocksField';
|
|
10
|
+
export { default as WxBlocksPage } from './BlocksPage';
|
|
11
|
+
export { default as WxBlockEditorPage } from './BlockEditorPage';
|
|
12
|
+
export { default as WxBlockPicker } from './BlockPicker';
|
|
13
|
+
export { default as WxBlockThumb } from './BlockThumb';
|
|
14
|
+
export { default as WxBlockStage } from './BlockStage';
|
|
15
|
+
export type { BlockContent, BlockInput, BlockNode, BlockSource, BlocksMeta, BlockThumbnail, BlockType, BlockUsage, BlockVersion, BlockVersionMeta, Lint, PublishRefusal, RenderInput, RenderResult, } from './types';
|