@webiny/website-builder-vue 6.4.0-beta.4
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/index.js +4 -0
- package/package.json +46 -0
- package/src/components/ConnectToEditor.ts +46 -0
- package/src/components/DocumentFragment.ts +43 -0
- package/src/components/DocumentRenderer.ts +102 -0
- package/src/components/DocumentStoreProvider.ts +53 -0
- package/src/components/EditingElementRenderer/EditingElementRenderer.presenter.ts +59 -0
- package/src/components/EditingElementRenderer/EditingElementRenderer.ts +70 -0
- package/src/components/EditingElementRenderer/index.ts +2 -0
- package/src/components/ElementIndexProvider.ts +24 -0
- package/src/components/ElementRenderer.ts +42 -0
- package/src/components/ElementSlot.ts +34 -0
- package/src/components/ElementSlotDepthProvider.ts +24 -0
- package/src/components/FragmentsProvider.ts +87 -0
- package/src/components/LiveElementRenderer.ts +102 -0
- package/src/components/LiveElementSlot.ts +46 -0
- package/src/components/PreviewElementSlot.ts +43 -0
- package/src/components/index.ts +17 -0
- package/src/composables/useBindingsForElement.ts +40 -0
- package/src/composables/useDocumentState.ts +13 -0
- package/src/composables/useObservable.ts +30 -0
- package/src/composables/useSelectFromState.ts +18 -0
- package/src/composables/useViewport.ts +27 -0
- package/src/createComponent.ts +55 -0
- package/src/editorComponents/Box.manifest.ts +19 -0
- package/src/editorComponents/Box.ts +8 -0
- package/src/editorComponents/Fragment.manifest.ts +19 -0
- package/src/editorComponents/Fragment.ts +57 -0
- package/src/editorComponents/Grid.manifest.ts +166 -0
- package/src/editorComponents/Grid.ts +72 -0
- package/src/editorComponents/GridColumn.manifest.ts +23 -0
- package/src/editorComponents/GridColumn.ts +6 -0
- package/src/editorComponents/Image.manifest.ts +36 -0
- package/src/editorComponents/Image.ts +144 -0
- package/src/editorComponents/Lexical.manifest.ts +24 -0
- package/src/editorComponents/Lexical.ts +23 -0
- package/src/editorComponents/Root.manifest.ts +10 -0
- package/src/editorComponents/Root.ts +8 -0
- package/src/editorComponents/index.ts +12 -0
- package/src/index.ts +52 -0
- package/src/lexical.css +379 -0
- package/src/types.ts +46 -0
- package/tsconfig.build.json +16 -0
- package/tsconfig.json +16 -0
- package/webiny.config.js +8 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { h } from "vue";
|
|
2
|
+
import type { ComponentProps } from "~/types.js";
|
|
3
|
+
import type { VNode } from "vue";
|
|
4
|
+
|
|
5
|
+
interface Column {
|
|
6
|
+
children: VNode | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type GridProps = ComponentProps<{
|
|
10
|
+
gridLayout: string;
|
|
11
|
+
rowCount: number;
|
|
12
|
+
rowGap: number;
|
|
13
|
+
columnGap: number;
|
|
14
|
+
columns: Column[];
|
|
15
|
+
stackAtBreakpoint?: string;
|
|
16
|
+
reverseWhenStacked?: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Renders a flexible CSS grid with rows/columns derived from `gridLayout`
|
|
21
|
+
* (e.g. "6-6" = two equal columns, "4-8" = one-third + two-thirds, etc.).
|
|
22
|
+
*
|
|
23
|
+
* The children of each column are VNodes resolved by the SDK (ElementSlot).
|
|
24
|
+
*/
|
|
25
|
+
export const GridComponent = (props: GridProps) => {
|
|
26
|
+
const { inputs, styles, breakpoint } = props;
|
|
27
|
+
const {
|
|
28
|
+
gridLayout = "12",
|
|
29
|
+
columns = [],
|
|
30
|
+
columnGap,
|
|
31
|
+
stackAtBreakpoint,
|
|
32
|
+
reverseWhenStacked
|
|
33
|
+
} = inputs;
|
|
34
|
+
|
|
35
|
+
const rowConfig = gridLayout.split("-").map(s => parseInt(s));
|
|
36
|
+
const rows: Column[][] = [];
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < columns.length; i += rowConfig.length) {
|
|
39
|
+
rows.push(columns.slice(i, i + rowConfig.length));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const cellWidthReduction = columnGap ? columnGap - columnGap / rowConfig.length : 0;
|
|
43
|
+
const stackColumns = breakpoint === stackAtBreakpoint;
|
|
44
|
+
|
|
45
|
+
const gridStyles = { ...styles };
|
|
46
|
+
if (stackColumns) {
|
|
47
|
+
gridStyles.flexDirection = reverseWhenStacked ? "column-reverse" : "column";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const cells = rows.flatMap(rowCols =>
|
|
51
|
+
rowCols.map((col, i) => {
|
|
52
|
+
const width = stackColumns
|
|
53
|
+
? "100%"
|
|
54
|
+
: `calc(${(rowConfig[i] / 12) * 100}% - ${cellWidthReduction}px)`;
|
|
55
|
+
|
|
56
|
+
return h(
|
|
57
|
+
"div",
|
|
58
|
+
{
|
|
59
|
+
key: i,
|
|
60
|
+
style: {
|
|
61
|
+
flex: `0 0 ${width}`,
|
|
62
|
+
maxWidth: width,
|
|
63
|
+
boxSizing: "border-box"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
[col.children]
|
|
67
|
+
);
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return h("div", { style: gridStyles }, cells);
|
|
72
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { StyleSettings } from "@webiny/website-builder-sdk";
|
|
3
|
+
import { createComponent } from "~/createComponent.js";
|
|
4
|
+
import { GridColumnComponent } from "./GridColumn.js";
|
|
5
|
+
|
|
6
|
+
export const GridColumn = createComponent(GridColumnComponent, {
|
|
7
|
+
name: "Webiny/GridColumn",
|
|
8
|
+
label: "Column",
|
|
9
|
+
image: `<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M600-120q-33 0-56.5-23.5T520-200v-560q0-33 23.5-56.5T600-840h160q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H600Zm0-640v560h160v-560H600ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h160q33 0 56.5 23.5T440-760v560q0 33-23.5 56.5T360-120H200Zm0-640v560h160v-560H200Zm560 0H600h160Zm-400 0H200h160Z"/></svg>`,
|
|
10
|
+
canDrag: false,
|
|
11
|
+
canDelete: false,
|
|
12
|
+
acceptsChildren: true,
|
|
13
|
+
hideFromToolbar: true,
|
|
14
|
+
hideStyleSettings: [StyleSettings.Visibility],
|
|
15
|
+
defaults: {
|
|
16
|
+
styles: {
|
|
17
|
+
paddingTop: "10px",
|
|
18
|
+
paddingRight: "10px",
|
|
19
|
+
paddingBottom: "10px",
|
|
20
|
+
paddingLeft: "10px"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createFileInput, createTextInput } from "@webiny/website-builder-sdk";
|
|
3
|
+
import { createComponent } from "~/createComponent.js";
|
|
4
|
+
import { ImageComponent } from "./Image.js";
|
|
5
|
+
|
|
6
|
+
// ImageComponent is a defineComponent result (object), not a plain function.
|
|
7
|
+
// createComponent accepts any component type at runtime; cast to satisfy the
|
|
8
|
+
// type constraint which is only used for input-type inference.
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
export const Image = createComponent(ImageComponent as unknown as (props: any) => any, {
|
|
11
|
+
name: "Webiny/Image",
|
|
12
|
+
label: "Image",
|
|
13
|
+
group: "basic",
|
|
14
|
+
image: `<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm40-80h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z"/></svg>`,
|
|
15
|
+
autoApplyStyles: false,
|
|
16
|
+
inputs: {
|
|
17
|
+
image: createFileInput({
|
|
18
|
+
label: "Image",
|
|
19
|
+
allowedFileTypes: ["image/*"],
|
|
20
|
+
onChange: ({ inputs }) => {
|
|
21
|
+
if (inputs.image) {
|
|
22
|
+
inputs.title = inputs.image.name;
|
|
23
|
+
inputs.altText = inputs.image.name;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}),
|
|
27
|
+
title: createTextInput({ label: "Title", description: "Title of the image" }),
|
|
28
|
+
altText: createTextInput({
|
|
29
|
+
label: "Alternate Text",
|
|
30
|
+
description: "Shown when the user has disabled images"
|
|
31
|
+
})
|
|
32
|
+
},
|
|
33
|
+
defaults: {
|
|
34
|
+
styles: { width: "100%" }
|
|
35
|
+
}
|
|
36
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { defineComponent, ref, watch, h, Fragment, type PropType } from "vue";
|
|
2
|
+
import { contentSdk } from "@webiny/website-builder-sdk";
|
|
3
|
+
import type { CssProperties } from "@webiny/website-builder-sdk";
|
|
4
|
+
|
|
5
|
+
const SUPPORTED_WIDTHS = [100, 300, 500, 750, 1000, 1500, 2500];
|
|
6
|
+
|
|
7
|
+
type ImageInputs = {
|
|
8
|
+
title: string;
|
|
9
|
+
altText: string;
|
|
10
|
+
image: {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
size: number;
|
|
14
|
+
mimeType: string;
|
|
15
|
+
src: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getSrcSet = (src: string, widths: number[]) =>
|
|
20
|
+
widths.map(w => `${src}?width=${w} ${w}w`).join(", ");
|
|
21
|
+
|
|
22
|
+
const computeSrcSetWidths = (width?: string | number): number[] => {
|
|
23
|
+
if (width && String(width).endsWith("px")) {
|
|
24
|
+
const px = parseInt(String(width));
|
|
25
|
+
const widths: number[] = [];
|
|
26
|
+
for (const w of SUPPORTED_WIDTHS) {
|
|
27
|
+
widths.push(w);
|
|
28
|
+
if (w >= px) {
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return widths;
|
|
33
|
+
}
|
|
34
|
+
return SUPPORTED_WIDTHS;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const ImagePlaceholder = (props: { style: CssProperties }) =>
|
|
38
|
+
h(
|
|
39
|
+
"div",
|
|
40
|
+
{
|
|
41
|
+
style: {
|
|
42
|
+
display: "flex",
|
|
43
|
+
height: "200px",
|
|
44
|
+
backgroundColor: "#f4f4f4",
|
|
45
|
+
justifyContent: "center",
|
|
46
|
+
alignItems: "center",
|
|
47
|
+
fill: "#ffffff",
|
|
48
|
+
...props.style
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
[
|
|
52
|
+
h(
|
|
53
|
+
"svg",
|
|
54
|
+
{
|
|
55
|
+
style: {
|
|
56
|
+
width: "70px",
|
|
57
|
+
height: "70px",
|
|
58
|
+
filter: "drop-shadow(rgba(0, 0, 0, 0.16) 0px 1px 0px)"
|
|
59
|
+
},
|
|
60
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
61
|
+
height: "24px",
|
|
62
|
+
viewBox: "0 -960 960 960",
|
|
63
|
+
width: "24px"
|
|
64
|
+
},
|
|
65
|
+
[
|
|
66
|
+
h("path", {
|
|
67
|
+
d: "M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm40-80h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z"
|
|
68
|
+
})
|
|
69
|
+
]
|
|
70
|
+
)
|
|
71
|
+
]
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
export const ImageComponent = defineComponent({
|
|
75
|
+
name: "WebinyImageComponent",
|
|
76
|
+
props: {
|
|
77
|
+
inputs: { type: Object as PropType<ImageInputs>, required: true },
|
|
78
|
+
styles: { type: Object as PropType<CssProperties>, default: () => ({}) }
|
|
79
|
+
},
|
|
80
|
+
setup(props) {
|
|
81
|
+
// In editing mode start as not-loaded (fade in after load).
|
|
82
|
+
// In live mode start as loaded (no fade effect needed).
|
|
83
|
+
const isLoaded = ref(!contentSdk.isEditing());
|
|
84
|
+
|
|
85
|
+
watch(
|
|
86
|
+
() => props.inputs?.image?.src,
|
|
87
|
+
src => {
|
|
88
|
+
if (!src) {
|
|
89
|
+
isLoaded.value = false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
function onLoad() {
|
|
95
|
+
if (contentSdk.isEditing()) {
|
|
96
|
+
setTimeout(() => {
|
|
97
|
+
isLoaded.value = true;
|
|
98
|
+
}, 100);
|
|
99
|
+
} else {
|
|
100
|
+
isLoaded.value = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
const { inputs, styles } = props;
|
|
106
|
+
const { title = "", altText, image } = inputs;
|
|
107
|
+
const src = image?.src;
|
|
108
|
+
|
|
109
|
+
if (!src) {
|
|
110
|
+
return h(ImagePlaceholder, { style: styles });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (src.endsWith(".svg")) {
|
|
114
|
+
return h("object", {
|
|
115
|
+
style: { maxWidth: "100%", ...styles },
|
|
116
|
+
title,
|
|
117
|
+
data: src
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
+
const imageStyles: CssProperties = {
|
|
123
|
+
maxWidth: "100%",
|
|
124
|
+
opacity: isLoaded.value ? 1 : 0,
|
|
125
|
+
transition: "opacity 0.3s ease",
|
|
126
|
+
...styles
|
|
127
|
+
} as any;
|
|
128
|
+
|
|
129
|
+
const srcSet = getSrcSet(src, computeSrcSetWidths(styles.width));
|
|
130
|
+
|
|
131
|
+
return h(Fragment, null, [
|
|
132
|
+
!isLoaded.value ? h(ImagePlaceholder, { style: styles }) : null,
|
|
133
|
+
h("img", {
|
|
134
|
+
alt: altText,
|
|
135
|
+
title,
|
|
136
|
+
src,
|
|
137
|
+
srcset: srcSet,
|
|
138
|
+
style: imageStyles,
|
|
139
|
+
onLoad
|
|
140
|
+
})
|
|
141
|
+
]);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createLexicalInput } from "@webiny/website-builder-sdk";
|
|
3
|
+
import { createComponent } from "~/createComponent.js";
|
|
4
|
+
import { LexicalComponent, createLexicalValue } from "./Lexical.js";
|
|
5
|
+
|
|
6
|
+
export const Lexical = createComponent(LexicalComponent, {
|
|
7
|
+
name: "Webiny/Lexical",
|
|
8
|
+
label: "Rich Text",
|
|
9
|
+
group: "basic",
|
|
10
|
+
image: `<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px"><path d="M280-160v-520H80v-120h520v120H400v520H280Zm360 0v-320H520v-120h360v120H760v320H640Z"/></svg>`,
|
|
11
|
+
inputs: [
|
|
12
|
+
createLexicalInput({
|
|
13
|
+
name: "content",
|
|
14
|
+
label: "Content"
|
|
15
|
+
})
|
|
16
|
+
],
|
|
17
|
+
defaults: {
|
|
18
|
+
inputs: {
|
|
19
|
+
content: createLexicalValue(
|
|
20
|
+
"Examine she brother prudent add day ham. Far stairs now coming bed oppose hunted become his. You zealously departure had procuring suspicion. Books whose front would purse if be do decay. Quitting you way formerly disposed perceive ladyship are. Common turned boy direct and yet."
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { h } from "vue";
|
|
2
|
+
import type { ComponentProps } from "~/types.js";
|
|
3
|
+
|
|
4
|
+
export const createLexicalValue = (value: string) => ({
|
|
5
|
+
state: `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"${value}","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"wby-paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`,
|
|
6
|
+
html: `<p class="wb-paragraph-1">${value}</p>`
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
type LexicalProps = ComponentProps<{
|
|
10
|
+
content: { html?: string };
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Renders Lexical rich-text content as raw HTML.
|
|
15
|
+
* The HTML is produced by the editor and stored in `inputs.content.html`.
|
|
16
|
+
*/
|
|
17
|
+
export const LexicalComponent = (props: LexicalProps) => {
|
|
18
|
+
const html = props.inputs?.content?.html;
|
|
19
|
+
if (!html) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return h("div", { innerHTML: html });
|
|
23
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createComponent } from "~/createComponent.js";
|
|
3
|
+
import { RootComponent } from "./Root.js";
|
|
4
|
+
|
|
5
|
+
export const Root = createComponent(RootComponent, {
|
|
6
|
+
name: "Webiny/Root",
|
|
7
|
+
label: "Main Content",
|
|
8
|
+
acceptsChildren: true,
|
|
9
|
+
hideFromToolbar: true
|
|
10
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Box } from "./Box.manifest.js";
|
|
2
|
+
import { Fragment } from "./Fragment.manifest.js";
|
|
3
|
+
import { Grid } from "./Grid.manifest.js";
|
|
4
|
+
import { GridColumn } from "./GridColumn.manifest.js";
|
|
5
|
+
import { Image } from "./Image.manifest.js";
|
|
6
|
+
import { Lexical } from "./Lexical.manifest.js";
|
|
7
|
+
import { Root } from "./Root.manifest.js";
|
|
8
|
+
|
|
9
|
+
export const editorComponents = [Root, Box, Grid, GridColumn, Image, Lexical, Fragment];
|
|
10
|
+
|
|
11
|
+
export { Box, Fragment, Grid, GridColumn, Image, Lexical, Root };
|
|
12
|
+
export { createLexicalValue } from "./Lexical.js";
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Re-export the full framework-agnostic SDK surface (types, inputs, SDK instance, etc.)
|
|
2
|
+
export {
|
|
3
|
+
createTextInput,
|
|
4
|
+
createLongTextInput,
|
|
5
|
+
createNumberInput,
|
|
6
|
+
createBooleanInput,
|
|
7
|
+
createColorInput,
|
|
8
|
+
createFileInput,
|
|
9
|
+
createDateInput,
|
|
10
|
+
createLexicalInput,
|
|
11
|
+
createSelectInput,
|
|
12
|
+
createRadioInput,
|
|
13
|
+
createObjectInput,
|
|
14
|
+
createTagsInput,
|
|
15
|
+
createSlotInput,
|
|
16
|
+
createInput,
|
|
17
|
+
createElement,
|
|
18
|
+
createTheme,
|
|
19
|
+
contentSdk,
|
|
20
|
+
environment,
|
|
21
|
+
setHeadersProvider,
|
|
22
|
+
getHeadersProvider,
|
|
23
|
+
registerComponentGroup,
|
|
24
|
+
type CssProperties,
|
|
25
|
+
type Document,
|
|
26
|
+
type DocumentElement,
|
|
27
|
+
type Breakpoint,
|
|
28
|
+
type CreateElementParams,
|
|
29
|
+
type ContentSDKConfig,
|
|
30
|
+
type ComponentManifest,
|
|
31
|
+
type ComponentInput,
|
|
32
|
+
type ComponentConstraint,
|
|
33
|
+
type WebsiteBuilderThemeInput,
|
|
34
|
+
StyleSettings
|
|
35
|
+
} from "@webiny/website-builder-sdk";
|
|
36
|
+
|
|
37
|
+
// Vue-specific exports
|
|
38
|
+
export { createComponent } from "./createComponent.js";
|
|
39
|
+
export * from "./components/index.js";
|
|
40
|
+
export * from "./editorComponents/index.js";
|
|
41
|
+
export * from "./composables/useViewport.js";
|
|
42
|
+
export * from "./composables/useSelectFromState.js";
|
|
43
|
+
export * from "./composables/useObservable.js";
|
|
44
|
+
export * from "./composables/useBindingsForElement.js";
|
|
45
|
+
export * from "./composables/useDocumentState.js";
|
|
46
|
+
export type {
|
|
47
|
+
ComponentProps,
|
|
48
|
+
ComponentPropsWithChildren,
|
|
49
|
+
InferManifest,
|
|
50
|
+
InferComponentChange,
|
|
51
|
+
InferDescendantChange
|
|
52
|
+
} from "./types.js";
|