@pairbo/ui-kit 0.0.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/.husky/pre-commit +1 -0
- package/.prettierignore +16 -0
- package/.prettierrc.json +17 -0
- package/README.md +61 -0
- package/cspell.json +9 -0
- package/dev.html +101 -0
- package/docs/README.md +1 -0
- package/docs/_includes/component.njk +16 -0
- package/docs/_includes/default.njk +39 -0
- package/docs/_includes/sidebar.njk +16 -0
- package/docs/eleventy.config.mjs +72 -0
- package/docs/pages/components/message-selector.md +17 -0
- package/docs/pages/fabric-example.html +46 -0
- package/docs/pages/fabric-example.js +28 -0
- package/docs/pages/index.md +76 -0
- package/eslint.config.mjs +32 -0
- package/ignote_temp +3 -0
- package/index.html +162 -0
- package/lint-stage.confg.js +6 -0
- package/package.json +66 -0
- package/pages/card-selection.html +65 -0
- package/pages/drawer.html +47 -0
- package/pages/editor.html +45 -0
- package/pages/page-mgn.html +51 -0
- package/pages/test_build.html +47 -0
- package/public/Greeting Card from Pairbo.png +0 -0
- package/scripts/plop/plopfile.js +51 -0
- package/scripts/plop/templates/components/component.hbs +34 -0
- package/scripts/plop/templates/components/define.hbs +10 -0
- package/scripts/plop/templates/components/styles.hbs +7 -0
- package/src/components/button/button.component.ts +93 -0
- package/src/components/button/button.styles.ts +273 -0
- package/src/components/button/button.ts +10 -0
- package/src/components/button-group/button-group.component.ts +36 -0
- package/src/components/button-group/button-group.styles.ts +7 -0
- package/src/components/button-group/button-group.ts +10 -0
- package/src/components/card-selection/card-selection.component.ts +43 -0
- package/src/components/card-selection/card-selection.styles.ts +7 -0
- package/src/components/card-selection/card-selection.ts +10 -0
- package/src/components/category/category.component.ts +91 -0
- package/src/components/category/category.styles.ts +27 -0
- package/src/components/category/category.ts +10 -0
- package/src/components/category-image/category-image.component.ts +38 -0
- package/src/components/category-image/category-image.styles.ts +11 -0
- package/src/components/category-image/category-image.ts +10 -0
- package/src/components/drawer/drawer.component.ts +82 -0
- package/src/components/drawer/drawer.styles.ts +54 -0
- package/src/components/drawer/drawer.ts +10 -0
- package/src/components/editor/editor.component.ts +135 -0
- package/src/components/editor/editor.styles.ts +13 -0
- package/src/components/editor/editor.ts +10 -0
- package/src/components/fabric-example/fabric-example.component.ts +268 -0
- package/src/components/fabric-example/fabric-example.styles.ts +23 -0
- package/src/components/fabric-example/fabric-example.test.ts +0 -0
- package/src/components/fabric-example/fabric-example.ts +12 -0
- package/src/components/image-slider/editor-card-slider.component.ts +136 -0
- package/src/components/image-slider/editor-card-slider.styles.ts +46 -0
- package/src/components/image-slider/editor-card-slider.ts +9 -0
- package/src/components/main.ts +17 -0
- package/src/components/message-selector/message-selector.component.ts +154 -0
- package/src/components/message-selector/message-selector.styles.ts +16 -0
- package/src/components/message-selector/message-selector.test.ts +64 -0
- package/src/components/message-selector/message-selector.ts +13 -0
- package/src/components/page-manager/page-manager.component.ts +228 -0
- package/src/components/page-manager/page-manager.styles.ts +9 -0
- package/src/components/page-manager/page-manager.ts +10 -0
- package/src/components/radio-button/radio-button.component.ts +118 -0
- package/src/components/radio-button/radio-button.styles.ts +13 -0
- package/src/components/radio-button/radio-button.ts +10 -0
- package/src/components/radio-group/radio-group.component.ts +203 -0
- package/src/components/radio-group/radio-group.styles.ts +19 -0
- package/src/components/radio-group/radio-group.ts +10 -0
- package/src/components/selector/selector.component.ts +115 -0
- package/src/components/selector/selector.styles.ts +9 -0
- package/src/components/selector/selector.ts +10 -0
- package/src/components/textarea/textarea.component.ts +234 -0
- package/src/components/textarea/textarea.styles.ts +178 -0
- package/src/components/textarea/textarea.ts +10 -0
- package/src/components/type-form/type-form.component.ts +121 -0
- package/src/components/type-form/type-form.styles.ts +7 -0
- package/src/components/type-form/type-form.ts +10 -0
- package/src/declaration.d.ts +44 -0
- package/src/events/events.ts +1 -0
- package/src/events/pbo-category-card-select.ts +7 -0
- package/src/internal/form.ts +376 -0
- package/src/internal/pairbo-element.ts +85 -0
- package/src/internal/slots.ts +54 -0
- package/src/internal/watch.ts +79 -0
- package/src/styles/component.styles.ts +17 -0
- package/src/styles/form-control.styles.ts +59 -0
- package/src/themes/default.css +414 -0
- package/temp +20 -0
- package/tsconfig.json +28 -0
- package/vite.config.ts +26 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { customElement, property, query } from "lit/decorators.js";
|
|
2
|
+
import { html, LitElement } from "lit";
|
|
3
|
+
import type { CSSResultGroup, PropertyValues } from "lit";
|
|
4
|
+
import componentStyles from "../../styles/component.styles.js";
|
|
5
|
+
import PairboElement from "../../internal/pairbo-element.js";
|
|
6
|
+
import styles from "./drawer.styles.js";
|
|
7
|
+
import { classMap } from "lit/directives/class-map.js";
|
|
8
|
+
import { watch } from "../../internal/watch.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @summary Short summary of the component's intended use.
|
|
12
|
+
* @status experimental
|
|
13
|
+
*
|
|
14
|
+
* @dependency pbo-example
|
|
15
|
+
*
|
|
16
|
+
* @event pbo-event-name - Emitted as an example.
|
|
17
|
+
*
|
|
18
|
+
* @slot - The default slot.
|
|
19
|
+
* @slot example - An example slot.
|
|
20
|
+
*
|
|
21
|
+
* @csspart base - The component's base wrapper.
|
|
22
|
+
*
|
|
23
|
+
* @cssproperty --example - An example CSS custom property.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
@customElement("pbo-drawer")
|
|
27
|
+
export default class PboModal extends PairboElement {
|
|
28
|
+
static styles: CSSResultGroup = [componentStyles, styles];
|
|
29
|
+
|
|
30
|
+
@query(".drawer") drawer!: HTMLDivElement;
|
|
31
|
+
@property({ type: Boolean, reflect: true }) open = false;
|
|
32
|
+
|
|
33
|
+
private requestClose(source: "close-button" | "keyboard" | "overlay") {
|
|
34
|
+
this.hide();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async hide() {
|
|
38
|
+
this.open = false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async show() {
|
|
42
|
+
if (this.open) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
this.open = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@watch("open", { waitUntilFirstUpdate: true })
|
|
49
|
+
handleOpenChange() {
|
|
50
|
+
if (this.open) this.drawer.hidden = false;
|
|
51
|
+
else this.drawer.hidden = true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Init the component
|
|
55
|
+
protected firstUpdated(): void {
|
|
56
|
+
this.drawer.hidden = !this.open;
|
|
57
|
+
}
|
|
58
|
+
render() {
|
|
59
|
+
return html`
|
|
60
|
+
<div
|
|
61
|
+
part="base"
|
|
62
|
+
class=${classMap({
|
|
63
|
+
drawer: true,
|
|
64
|
+
"drawer--bottom": true,
|
|
65
|
+
"drawer--open": this.open,
|
|
66
|
+
})}
|
|
67
|
+
>
|
|
68
|
+
<div part="overlay" class="drawer__overlay" @click=${() => this.requestClose("overlay")}></div>
|
|
69
|
+
<div
|
|
70
|
+
part="panel"
|
|
71
|
+
class="drawer__panel"
|
|
72
|
+
role="dialog"
|
|
73
|
+
aria-modal="true"
|
|
74
|
+
aria-hidden=${this.open ? "false" : "true"}
|
|
75
|
+
tabindex="0"
|
|
76
|
+
>
|
|
77
|
+
<slot part="body" class="drawer__body"></slot>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export default css`
|
|
4
|
+
:host {
|
|
5
|
+
--size: 30rem;
|
|
6
|
+
--header-spacing: var(--sl-spacing-large);
|
|
7
|
+
--body-spacing: var(--sl-spacing-large);
|
|
8
|
+
--footer-spacing: var(--sl-spacing-large);
|
|
9
|
+
|
|
10
|
+
display: contents;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.drawer {
|
|
14
|
+
top: 0;
|
|
15
|
+
inset-inline-start: 0;
|
|
16
|
+
width: 100%;
|
|
17
|
+
height: 100%;
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.drawer__overlay {
|
|
23
|
+
display: block;
|
|
24
|
+
position: fixed;
|
|
25
|
+
top: 0;
|
|
26
|
+
right: 0;
|
|
27
|
+
bottom: 0;
|
|
28
|
+
left: 0;
|
|
29
|
+
background-color: var(--sl-overlay-background-color);
|
|
30
|
+
pointer-events: all;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.drawer__panel {
|
|
34
|
+
position: absolute;
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
z-index: 2;
|
|
38
|
+
max-width: 100%;
|
|
39
|
+
max-height: 100%;
|
|
40
|
+
background-color: var(--sl-panel-background-color);
|
|
41
|
+
box-shadow: var(--sl-shadow-x-large);
|
|
42
|
+
overflow: auto;
|
|
43
|
+
pointer-events: all;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.drawer--bottom .drawer__panel {
|
|
47
|
+
top: auto;
|
|
48
|
+
inset-inline-end: auto;
|
|
49
|
+
bottom: 0;
|
|
50
|
+
inset-inline-start: 0;
|
|
51
|
+
width: 100%;
|
|
52
|
+
height: var(--size);
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { customElement, property, query, state } from "lit/decorators.js";
|
|
2
|
+
import { html, LitElement } from "lit";
|
|
3
|
+
import type { CSSResultGroup, PropertyValues } from "lit";
|
|
4
|
+
import componentStyles from "../../styles/component.styles.js";
|
|
5
|
+
import PairboElement from "../../internal/pairbo-element.js";
|
|
6
|
+
import styles from "./editor.styles.js";
|
|
7
|
+
import FabricExample from "../fabric-example/fabric-example.component.js";
|
|
8
|
+
import PboTypeForm from "../type-form/type-form.component.js";
|
|
9
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
10
|
+
import PboEditorCardSlider from "../image-slider/editor-card-slider.component.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @summary Short summary of the component's intended use.
|
|
14
|
+
* @status experimental
|
|
15
|
+
*
|
|
16
|
+
* @dependency pbo-example
|
|
17
|
+
*
|
|
18
|
+
* @event pbo-event-name - Emitted as an example.
|
|
19
|
+
*
|
|
20
|
+
* @slot - The default slot.
|
|
21
|
+
* @slot example - An example slot.
|
|
22
|
+
*
|
|
23
|
+
* @csspart base - The component's base wrapper.
|
|
24
|
+
*
|
|
25
|
+
* @cssproperty --example - An example CSS custom property.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
interface FormData {
|
|
29
|
+
cardId: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface TypingFormData extends FormData {
|
|
33
|
+
font: string;
|
|
34
|
+
text: string;
|
|
35
|
+
color: string;
|
|
36
|
+
alignment: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@customElement("pbo-editor")
|
|
40
|
+
export default class PboEditor extends PairboElement {
|
|
41
|
+
static styles: CSSResultGroup = [componentStyles, styles];
|
|
42
|
+
static dependencies = {
|
|
43
|
+
"editor-card-slider": PboEditorCardSlider,
|
|
44
|
+
"fabric-example": FabricExample,
|
|
45
|
+
"pbo-type-form": PboTypeForm,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
@property({ type: String, reflect: true }) cardInnerImageUrl = "../../../public/Greeting Card from Pairbo.png";
|
|
49
|
+
|
|
50
|
+
@query("pbo-type-form") typingForm!: PboTypeForm;
|
|
51
|
+
private handleTypingFormChange() {
|
|
52
|
+
console.log(this.typingForm.form);
|
|
53
|
+
}
|
|
54
|
+
@query("pbo-editor-card-slider") slider!: PboEditorCardSlider;
|
|
55
|
+
// private _card: Card | null = null;
|
|
56
|
+
|
|
57
|
+
@property({ type: Object }) card: Card | null = null;
|
|
58
|
+
@state()
|
|
59
|
+
private formData = { font: "Monsieur La Doulaise", text: "", color: "rgb(0,0,0)", alignment: "left" };
|
|
60
|
+
|
|
61
|
+
protected firstUpdated(): void {
|
|
62
|
+
this.typingForm.addEventListener("pbo-change", event => {
|
|
63
|
+
this.formData = this.typingForm.form;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
isCardType(obj: any): obj is Card {
|
|
68
|
+
return (
|
|
69
|
+
obj &&
|
|
70
|
+
typeof obj === "object" &&
|
|
71
|
+
"id" in obj &&
|
|
72
|
+
"name" in obj &&
|
|
73
|
+
"category" in obj &&
|
|
74
|
+
"medias" in obj &&
|
|
75
|
+
typeof obj.medias === "object" &&
|
|
76
|
+
"cover" in obj.medias &&
|
|
77
|
+
"render_1" in obj.medias &&
|
|
78
|
+
"render_2" in obj.medias &&
|
|
79
|
+
"inner" in obj.medias
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
updated(changedProperties: PropertyValues<this>): void {
|
|
84
|
+
if (changedProperties.has("cardInnerImageUrl")) {
|
|
85
|
+
this.requestUpdate();
|
|
86
|
+
}
|
|
87
|
+
if (changedProperties.has("card")) {
|
|
88
|
+
this.slider.card = this.card;
|
|
89
|
+
this.requestUpdate();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
disconnectedCallback(): void {
|
|
94
|
+
this.typingForm.removeEventListener("pbo-change", this.handleTypingFormChange);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
render() {
|
|
98
|
+
return html`
|
|
99
|
+
${JSON.stringify(this.card)} In the editor
|
|
100
|
+
<div class="editor">
|
|
101
|
+
<div
|
|
102
|
+
style=${styleMap({
|
|
103
|
+
maxWidth: "500px",
|
|
104
|
+
})}
|
|
105
|
+
>
|
|
106
|
+
<!--
|
|
107
|
+
<fabric-example
|
|
108
|
+
backgroundUrl=${this.cardInnerImageUrl}
|
|
109
|
+
alignment=${this.formData?.alignment || ""}
|
|
110
|
+
message=${this.formData?.text || ""}
|
|
111
|
+
color=${this.formData?.color || ""}
|
|
112
|
+
fontFamily=${this.formData?.font || ""}
|
|
113
|
+
></fabric-example>
|
|
114
|
+
-->
|
|
115
|
+
<pbo-editor-card-slider focusIndex="0" .livePreviewProps=${this.formData}></pbo-editor-card-slider>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div>
|
|
119
|
+
<pbo-type-form></pbo-type-form>
|
|
120
|
+
${JSON.stringify(this.formData)}
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
<button
|
|
124
|
+
@click=${() => {
|
|
125
|
+
const slider = this.shadowRoot?.querySelector("pbo-editor-card-slider");
|
|
126
|
+
if (slider && slider.getAttribute("focusIndex") !== "1") {
|
|
127
|
+
slider.setAttribute("focusIndex", "1");
|
|
128
|
+
}
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
Set main image to canvas
|
|
132
|
+
</button>
|
|
133
|
+
`;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { LitElement, html, css } from "lit";
|
|
2
|
+
import { customElement, property, query, state } from "lit/decorators.js";
|
|
3
|
+
import { Canvas, ITextEvents, SerializedTextboxProps, Textbox, TextboxProps, TOptions, FabricImage } from "fabric";
|
|
4
|
+
import { prototype } from "mocha";
|
|
5
|
+
|
|
6
|
+
@customElement("fabric-example")
|
|
7
|
+
export default class FabricExample extends LitElement {
|
|
8
|
+
@property() backgroundUrl: string =
|
|
9
|
+
"https://upload.wikimedia.org/wikipedia/commons/0/06/Daedalus_Spaceship_concept.jpg";
|
|
10
|
+
@property()
|
|
11
|
+
message: string = "Dear fridends,\n\nMy name is xxxxx. sadf;lkasdf\ngood aklsdfja";
|
|
12
|
+
@property({ type: String, reflect: true }) alignment: "left" | "center" | "right" = "left";
|
|
13
|
+
@property({ type: String }) fontFamily: string = "Arial";
|
|
14
|
+
@property({ type: String }) color: string = "#000";
|
|
15
|
+
|
|
16
|
+
@query(".fabric-container")
|
|
17
|
+
private _fabricContainer!: HTMLDivElement;
|
|
18
|
+
|
|
19
|
+
@state()
|
|
20
|
+
private _canvas?: Canvas;
|
|
21
|
+
@state()
|
|
22
|
+
private _canvasElement?: HTMLCanvasElement;
|
|
23
|
+
@state()
|
|
24
|
+
private _textbox?: Textbox<TOptions<TextboxProps>, SerializedTextboxProps, ITextEvents>;
|
|
25
|
+
@state()
|
|
26
|
+
private _backgroundImg: HTMLImageElement = new Image();
|
|
27
|
+
@state()
|
|
28
|
+
private _natureSize: { width: number; height: number } = { width: 0, height: 0 };
|
|
29
|
+
@state()
|
|
30
|
+
private _scale: number = 1;
|
|
31
|
+
@state()
|
|
32
|
+
private _lineHeight: number = 0;
|
|
33
|
+
@state()
|
|
34
|
+
private _fontSize: number = 0;
|
|
35
|
+
|
|
36
|
+
constructor() {
|
|
37
|
+
super();
|
|
38
|
+
this._backgroundImg = new Image();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static styles = css`
|
|
42
|
+
:host {
|
|
43
|
+
display: block;
|
|
44
|
+
height: 100%;
|
|
45
|
+
width: 100%;
|
|
46
|
+
}
|
|
47
|
+
.fabric-container {
|
|
48
|
+
display: block;
|
|
49
|
+
border: 2px solid #333;
|
|
50
|
+
border-radius: 4px;
|
|
51
|
+
background: #fff;
|
|
52
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
53
|
+
height: 100%;
|
|
54
|
+
width: 100%;
|
|
55
|
+
}
|
|
56
|
+
canvas {
|
|
57
|
+
border: 1px solid #ccc;
|
|
58
|
+
width: 100%;
|
|
59
|
+
height: 100%;
|
|
60
|
+
}
|
|
61
|
+
.grechen-fuemen-regular {
|
|
62
|
+
font-family: "Grechen Fuemen", serif;
|
|
63
|
+
font-weight: 400;
|
|
64
|
+
font-style: normal;
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
connectedCallback() {
|
|
68
|
+
super.connectedCallback();
|
|
69
|
+
this._backgroundImg.src = this.backgroundUrl;
|
|
70
|
+
this._backgroundImg.onload = () => {
|
|
71
|
+
this._natureSize = { width: this._backgroundImg.naturalWidth, height: this._backgroundImg.naturalHeight };
|
|
72
|
+
};
|
|
73
|
+
this._canvasElement = document.createElement("canvas");
|
|
74
|
+
}
|
|
75
|
+
formatTextbox(textbox: Textbox<TOptions<TextboxProps>, SerializedTextboxProps, ITextEvents>) {
|
|
76
|
+
console.log({
|
|
77
|
+
line: textbox?.textLines,
|
|
78
|
+
text: textbox?.text,
|
|
79
|
+
});
|
|
80
|
+
let lines = textbox.textLines;
|
|
81
|
+
console.log(lines.length);
|
|
82
|
+
if (lines.length > 12) {
|
|
83
|
+
// Prevent adding more lines after 12
|
|
84
|
+
const limitedLines = lines.slice(0, 12);
|
|
85
|
+
textbox.set("text", limitedLines.join("\n"));
|
|
86
|
+
textbox.setSelectionStart(textbox.text.length);
|
|
87
|
+
textbox.setSelectionEnd(textbox.text.length);
|
|
88
|
+
this._canvas?.renderAll();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
firstUpdated() {
|
|
93
|
+
const canvasElement = document.createElement("canvas");
|
|
94
|
+
this._fabricContainer.appendChild(canvasElement);
|
|
95
|
+
|
|
96
|
+
this._backgroundImg.onload = () => {
|
|
97
|
+
const fabricBackgroundImage = new FabricImage(this._backgroundImg);
|
|
98
|
+
|
|
99
|
+
this._canvas = new Canvas(canvasElement, {
|
|
100
|
+
backgroundImage: fabricBackgroundImage,
|
|
101
|
+
width: this._backgroundImg.naturalWidth,
|
|
102
|
+
height: this._backgroundImg.naturalHeight,
|
|
103
|
+
});
|
|
104
|
+
this._lineHeight = (0.45 * this._backgroundImg.height) / 12;
|
|
105
|
+
this._fontSize = this._lineHeight / 1.16;
|
|
106
|
+
|
|
107
|
+
const props: TOptions<TextboxProps> = {
|
|
108
|
+
fontSize: 16,
|
|
109
|
+
left: 0.084 * this._backgroundImg.naturalWidth,
|
|
110
|
+
// top: 0.4874 * this._backgroundImg.naturalHeight,
|
|
111
|
+
top: 0.52 * this._backgroundImg.naturalHeight,
|
|
112
|
+
width: 0.832 * this._backgroundImg.naturalWidth,
|
|
113
|
+
height: 0.45 * this._backgroundImg.naturalHeight,
|
|
114
|
+
hasControls: false,
|
|
115
|
+
editable: false,
|
|
116
|
+
breakWords: false,
|
|
117
|
+
splitByGrapheme: false, // Ensure text wraps correctly
|
|
118
|
+
textAlign: this.alignment,
|
|
119
|
+
lockMovementX: true,
|
|
120
|
+
lockMovementY: true,
|
|
121
|
+
selectable: false, // Make the textbox non-interactive
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
this._textbox = new Textbox(this.message, props);
|
|
125
|
+
this._textbox.set("fill", this.color);
|
|
126
|
+
// Custom logic to handle continuous characters without splitting words
|
|
127
|
+
this._textbox.on("changed", () => {
|
|
128
|
+
const maxLines = 12;
|
|
129
|
+
const maxCharsPerLine = 20;
|
|
130
|
+
if (!this._textbox) return;
|
|
131
|
+
let lines = this._textbox.text.split("\n");
|
|
132
|
+
let newLines: string[] = [];
|
|
133
|
+
lines.forEach(line => {
|
|
134
|
+
if (line.length > maxCharsPerLine) {
|
|
135
|
+
const words = line.split(" ");
|
|
136
|
+
let newLine = "";
|
|
137
|
+
let currentLength = 0;
|
|
138
|
+
words.forEach(word => {
|
|
139
|
+
if (currentLength + word.length > maxCharsPerLine) {
|
|
140
|
+
newLines.push(newLine.trim());
|
|
141
|
+
newLine = word + " ";
|
|
142
|
+
currentLength = word.length + 1;
|
|
143
|
+
} else {
|
|
144
|
+
newLine += word + " ";
|
|
145
|
+
currentLength += word.length + 1;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
if (newLine.trim().length > 0) {
|
|
149
|
+
newLines.push(newLine.trim());
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
newLines.push(line);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
if (newLines.length > maxLines) {
|
|
156
|
+
newLines = newLines.slice(0, maxLines);
|
|
157
|
+
}
|
|
158
|
+
this._textbox.text = newLines.join("\n");
|
|
159
|
+
this._textbox.setSelectionStart(this._textbox.text.length);
|
|
160
|
+
this._textbox.setSelectionEnd(this._textbox.text.length);
|
|
161
|
+
this._canvas?.renderAll();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Add text to canvas
|
|
165
|
+
this._canvas.add(this._textbox);
|
|
166
|
+
this._canvas.centerObject(this._textbox);
|
|
167
|
+
this._canvas.renderAll();
|
|
168
|
+
};
|
|
169
|
+
window.addEventListener("resize", () => {
|
|
170
|
+
canvasElement.width = this._fabricContainer.clientWidth;
|
|
171
|
+
canvasElement.height = this._fabricContainer.clientHeight;
|
|
172
|
+
this.adjustCanvasElements();
|
|
173
|
+
this._canvas?.renderAll(); // Re-render canvas content if necessary
|
|
174
|
+
});
|
|
175
|
+
setTimeout(() => this.adjustCanvasElements(), 100);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
adjustCanvasElements() {
|
|
179
|
+
if (this._canvas && this._textbox && this._backgroundImg && this._backgroundImg.complete) {
|
|
180
|
+
// Now we can safely work with the loaded image
|
|
181
|
+
|
|
182
|
+
const containerDiv = this.shadowRoot?.querySelector(".fabric-container");
|
|
183
|
+
if (!containerDiv) return;
|
|
184
|
+
const parentWidth = containerDiv.clientWidth;
|
|
185
|
+
const parentHeight = containerDiv.clientHeight;
|
|
186
|
+
const naturalWidth = this._backgroundImg.naturalWidth;
|
|
187
|
+
const naturalHeight = this._backgroundImg.naturalHeight;
|
|
188
|
+
const heightRatio = parentHeight / naturalHeight;
|
|
189
|
+
const widthRatio = parentWidth / naturalWidth;
|
|
190
|
+
const scale = Math.min(heightRatio, widthRatio);
|
|
191
|
+
this._scale = scale;
|
|
192
|
+
console.log({ parentWidth, parentHeight, naturalWidth, naturalHeight });
|
|
193
|
+
|
|
194
|
+
// Set the canvas dimensions
|
|
195
|
+
this._canvas.setWidth(naturalWidth * scale);
|
|
196
|
+
this._canvas.setHeight(naturalHeight * scale);
|
|
197
|
+
|
|
198
|
+
// Scale the background image
|
|
199
|
+
const background = this._canvas.backgroundImage;
|
|
200
|
+
if (background) {
|
|
201
|
+
background.scaleToWidth(naturalWidth * scale);
|
|
202
|
+
background.scaleToHeight(naturalHeight * scale);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Scale the textbox
|
|
206
|
+
this._textbox.set({
|
|
207
|
+
left: 0.084 * naturalWidth * scale,
|
|
208
|
+
top: 0.52 * naturalHeight * scale,
|
|
209
|
+
width: 0.832 * naturalWidth * scale,
|
|
210
|
+
height: 0.45 * naturalHeight * scale,
|
|
211
|
+
fontSize: this._fontSize * scale,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
console.log(((0.45 * naturalHeight) / 12) * 0.9 * scale);
|
|
215
|
+
console.log(`Line height pixel: ${this._textbox.get("lineHeight") * this._textbox.get("fontSize")}px`);
|
|
216
|
+
console.log(`Line height: ${this._textbox.get("lineHeight")}`);
|
|
217
|
+
console.log(`Font size: ${this._textbox.get("fontSize")}px`);
|
|
218
|
+
const lineHeight = (0.45 * naturalHeight * scale) / 12;
|
|
219
|
+
console.log(`desired line height: ${lineHeight}`);
|
|
220
|
+
console.log(`scale width: ${0.832 * naturalWidth * scale} - scale height: ${0.45 * naturalHeight * scale}`);
|
|
221
|
+
|
|
222
|
+
this._canvas.renderAll(); // Re-render canvas content if necessary
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
updated(changedProperties: Map<string | number | symbol, unknown>) {
|
|
227
|
+
console.log(changedProperties);
|
|
228
|
+
if (!this._textbox) return;
|
|
229
|
+
if (changedProperties.has("message")) {
|
|
230
|
+
this._textbox?.set("text", this.message);
|
|
231
|
+
this.formatTextbox(this._textbox);
|
|
232
|
+
this._canvas?.renderAll();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (changedProperties.has("alignment")) {
|
|
236
|
+
this._textbox?.set("textAlign", this.alignment);
|
|
237
|
+
this._canvas?.renderAll();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (changedProperties.has("fontFamily")) {
|
|
241
|
+
this._textbox?.set("fontFamily", this.fontFamily);
|
|
242
|
+
this._canvas?.renderAll();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (changedProperties.has("color")) {
|
|
246
|
+
this._textbox?.set("fill", this.color);
|
|
247
|
+
this._canvas?.renderAll();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
render() {
|
|
252
|
+
return html`
|
|
253
|
+
<div
|
|
254
|
+
style="position: fixed; top: 0; left: 0; padding: 1rem; z-index: 1000; background: rgba(255, 255, 255, 0.5); color: rgba(0, 0, 0, 0.5);"
|
|
255
|
+
>
|
|
256
|
+
Nature font size: ${this._fontSize} <br />
|
|
257
|
+
Nature line height: ${this._lineHeight} <br />
|
|
258
|
+
Line height: ${this._textbox?.get("lineHeight")} <br />
|
|
259
|
+
Font size: ${this._textbox?.get("fontSize")} <br />
|
|
260
|
+
real textbox width: ${this._backgroundImg.width * 0.382} <br />
|
|
261
|
+
real textbox height: ${this._backgroundImg.height * 0.45} <br />
|
|
262
|
+
Textbox width: ${this._textbox?.width} <br />
|
|
263
|
+
Textbox height: ${this._textbox?.height} <br />
|
|
264
|
+
</div>
|
|
265
|
+
<div class="fabric-container"></div>
|
|
266
|
+
`;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export default css`
|
|
4
|
+
:host {
|
|
5
|
+
display: block;
|
|
6
|
+
font-family: Arial, sans-serif;
|
|
7
|
+
}
|
|
8
|
+
.message-selector {
|
|
9
|
+
border: 1px solid #ccc;
|
|
10
|
+
padding: 1rem;
|
|
11
|
+
border-radius: 4px;
|
|
12
|
+
background-color: #fff;
|
|
13
|
+
color: black;
|
|
14
|
+
}
|
|
15
|
+
.fabric-container {
|
|
16
|
+
display: block;
|
|
17
|
+
margin-top: 1rem;
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 50%;
|
|
20
|
+
left: 50%;
|
|
21
|
+
transform: translate(-50%, -50%);
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import FabricExample from "./fabric-example.component.js";
|
|
2
|
+
|
|
3
|
+
export * from "./fabric-example.component.js";
|
|
4
|
+
export default FabricExample;
|
|
5
|
+
|
|
6
|
+
// customElements.define("pairbo-message-selector", PairboMessageSelector);
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
interface HTMLElementTagNameMap {
|
|
10
|
+
"fabric-example": FabricExample;
|
|
11
|
+
}
|
|
12
|
+
}
|