bsign-customization-full 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.
Files changed (126) hide show
  1. package/.env +2 -0
  2. package/components.json +21 -0
  3. package/dist/colors/anthracite-gray.webp +0 -0
  4. package/dist/colors/anthracite-gray_50x50.png +0 -0
  5. package/dist/colors/dark-wenge.webp +0 -0
  6. package/dist/colors/dark-wenge_50x50.png +0 -0
  7. package/dist/colors/indian-rosewood.webp +0 -0
  8. package/dist/colors/indian-rosewood_50x50.png +0 -0
  9. package/dist/colors/natural-wood.webp +0 -0
  10. package/dist/colors/natural-wood_50x50.png +0 -0
  11. package/dist/colors/redwood.webp +0 -0
  12. package/dist/colors/redwood_50x50.png +0 -0
  13. package/dist/colors/walnut.webp +0 -0
  14. package/dist/colors/walnut_50x50.png +0 -0
  15. package/dist/html2canvas.esm-BJ_egzt0.js +4802 -0
  16. package/dist/index-Dw5Zc1iD.js +33162 -0
  17. package/dist/index.es-Co1KNpGS.js +6681 -0
  18. package/dist/logo.png +0 -0
  19. package/dist/purify.es-CKpD2xIC.js +552 -0
  20. package/dist/sign-constructor.es.js +4 -0
  21. package/dist/sign-constructor.iife.js +171 -0
  22. package/dist/size-guide.webp +0 -0
  23. package/dist/size.webp +0 -0
  24. package/dist/templates/assets/modern/rectangle-black.webp +0 -0
  25. package/dist/templates/assets/modern/rectangle-white.webp +0 -0
  26. package/dist/templates/assets/modern/square-black.webp +0 -0
  27. package/dist/templates/assets/modern/square-white.webp +0 -0
  28. package/dist/templates/assets/wave.webp +0 -0
  29. package/dist/templates/jure.webp +0 -0
  30. package/dist/templates/modern.webp +0 -0
  31. package/dist/templates/sherwood.webp +0 -0
  32. package/dist/templates/wave.webp +0 -0
  33. package/eslint.config.js +23 -0
  34. package/index.html +13 -0
  35. package/modern-debug.svg +39 -0
  36. package/package.json +62 -0
  37. package/public/colors/anthracite-gray.webp +0 -0
  38. package/public/colors/anthracite-gray_50x50.png +0 -0
  39. package/public/colors/dark-wenge.webp +0 -0
  40. package/public/colors/dark-wenge_50x50.png +0 -0
  41. package/public/colors/indian-rosewood.webp +0 -0
  42. package/public/colors/indian-rosewood_50x50.png +0 -0
  43. package/public/colors/natural-wood.webp +0 -0
  44. package/public/colors/natural-wood_50x50.png +0 -0
  45. package/public/colors/redwood.webp +0 -0
  46. package/public/colors/redwood_50x50.png +0 -0
  47. package/public/colors/walnut.webp +0 -0
  48. package/public/colors/walnut_50x50.png +0 -0
  49. package/public/logo.png +0 -0
  50. package/public/size-guide.webp +0 -0
  51. package/public/size.webp +0 -0
  52. package/public/templates/assets/modern/rectangle-black.webp +0 -0
  53. package/public/templates/assets/modern/rectangle-white.webp +0 -0
  54. package/public/templates/assets/modern/square-black.webp +0 -0
  55. package/public/templates/assets/modern/square-white.webp +0 -0
  56. package/public/templates/assets/wave.webp +0 -0
  57. package/public/templates/jure.webp +0 -0
  58. package/public/templates/modern.webp +0 -0
  59. package/public/templates/sherwood.webp +0 -0
  60. package/public/templates/wave.webp +0 -0
  61. package/src/App.css +43 -0
  62. package/src/AppDemo2.tsx +257 -0
  63. package/src/components/cart-panel.tsx +170 -0
  64. package/src/components/cart-preview.tsx +356 -0
  65. package/src/components/cart-view.tsx +113 -0
  66. package/src/components/constructure-menu.tsx +37 -0
  67. package/src/components/header.tsx +214 -0
  68. package/src/components/heading.tsx +28 -0
  69. package/src/components/icons.tsx +54 -0
  70. package/src/components/import-file-modal.tsx +252 -0
  71. package/src/components/layers/grid-view.tsx +29 -0
  72. package/src/components/layers/image-layer.tsx +128 -0
  73. package/src/components/layers/layer-forms/material-form.tsx +53 -0
  74. package/src/components/layers/layer-forms/size-form.tsx +69 -0
  75. package/src/components/layers/layer-forms/template-form.tsx +39 -0
  76. package/src/components/layers/layer-forms/text-form.tsx +477 -0
  77. package/src/components/layers/layers-container.tsx +259 -0
  78. package/src/components/layers/text-layer.tsx +128 -0
  79. package/src/components/movable-item.tsx +228 -0
  80. package/src/components/preview.tsx +258 -0
  81. package/src/components/resize-button.tsx +83 -0
  82. package/src/components/size-guide-modal.tsx +47 -0
  83. package/src/components/size-guide.tsx +98 -0
  84. package/src/components/ui/button-group.tsx +83 -0
  85. package/src/components/ui/button.tsx +60 -0
  86. package/src/components/ui/checkbox.tsx +30 -0
  87. package/src/components/ui/dialog.tsx +151 -0
  88. package/src/components/ui/input-group.tsx +168 -0
  89. package/src/components/ui/input.tsx +21 -0
  90. package/src/components/ui/label.tsx +22 -0
  91. package/src/components/ui/popover.tsx +54 -0
  92. package/src/components/ui/progress.tsx +28 -0
  93. package/src/components/ui/radio-group.tsx +43 -0
  94. package/src/components/ui/scroll-area.tsx +56 -0
  95. package/src/components/ui/select.tsx +191 -0
  96. package/src/components/ui/separator.tsx +25 -0
  97. package/src/components/ui/sheet.tsx +141 -0
  98. package/src/components/ui/slider.tsx +61 -0
  99. package/src/components/ui/spinner.tsx +15 -0
  100. package/src/components/ui/textarea.tsx +18 -0
  101. package/src/components/ui/toggle-group.tsx +73 -0
  102. package/src/components/ui/toggle.tsx +45 -0
  103. package/src/components/ui/tooltip.tsx +67 -0
  104. package/src/fonts/BEBASNEUE-REGULAR.TTF +0 -0
  105. package/src/fonts/Braille-Regular.ttf +0 -0
  106. package/src/fonts/GOTHICB.TTF +0 -0
  107. package/src/hooks/use-mobile.ts +23 -0
  108. package/src/hooks/use-resize-constraints.ts +62 -0
  109. package/src/index.css +238 -0
  110. package/src/index.tsx +141 -0
  111. package/src/lib/cart-proposal-pdf.ts +350 -0
  112. package/src/lib/config-font.tsx +109 -0
  113. package/src/lib/config.ts +730 -0
  114. package/src/lib/pricing.ts +61 -0
  115. package/src/lib/type-checks.ts +47 -0
  116. package/src/lib/utils.ts +146 -0
  117. package/src/lib/widget-context.tsx +9 -0
  118. package/src/main.tsx +11 -0
  119. package/src/store/cart-store.ts +78 -0
  120. package/src/store/layers-store.ts +337 -0
  121. package/src/vite-env.d.ts +1 -0
  122. package/test/preview.html +37 -0
  123. package/tsconfig.app.json +33 -0
  124. package/tsconfig.json +13 -0
  125. package/tsconfig.node.json +25 -0
  126. package/vite.config.ts +38 -0
@@ -0,0 +1,62 @@
1
+ import { useCallback } from "react"
2
+
3
+ export interface ResizeConstraints {
4
+ minWidth?: number
5
+ maxWidth?: number
6
+ minAspectRatio?: number
7
+ maxAspectRatio?: number
8
+ sensitivity?: number
9
+ }
10
+
11
+ export function useResizeConstraints(constraints: ResizeConstraints = {}) {
12
+ const { minWidth = 10, maxWidth = 200, minAspectRatio = 0.1, maxAspectRatio = 10, sensitivity = 0.5 } = constraints
13
+
14
+ const constrainWidth = useCallback(
15
+ (width: number): number => {
16
+ return Math.max(minWidth, Math.min(maxWidth, width))
17
+ },
18
+ [minWidth, maxWidth],
19
+ )
20
+
21
+ const constrainAspectRatio = useCallback(
22
+ (ratio: number): number => {
23
+ return Math.max(minAspectRatio, Math.min(maxAspectRatio, ratio))
24
+ },
25
+ [minAspectRatio, maxAspectRatio],
26
+ )
27
+
28
+ const calculateWidthChange = useCallback(
29
+ (deltaX: number, deltaY?: number): number => {
30
+ let change = deltaX * sensitivity
31
+
32
+ // Add vertical influence for corner resizing
33
+ if (deltaY !== undefined) {
34
+ change += deltaY * sensitivity * 0.5
35
+ }
36
+
37
+ return change
38
+ },
39
+ [sensitivity],
40
+ )
41
+
42
+ const calculateAspectRatioChange = useCallback((deltaX?: number, deltaY?: number): number => {
43
+ const ratioSensitivity = 0.01
44
+
45
+ if (deltaX !== undefined) {
46
+ return deltaX * ratioSensitivity
47
+ }
48
+
49
+ if (deltaY !== undefined) {
50
+ return -deltaY * ratioSensitivity // Negative because moving down decreases ratio
51
+ }
52
+
53
+ return 0
54
+ }, [])
55
+
56
+ return {
57
+ constrainWidth,
58
+ constrainAspectRatio,
59
+ calculateWidthChange,
60
+ calculateAspectRatioChange,
61
+ }
62
+ }
package/src/index.css ADDED
@@ -0,0 +1,238 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ @font-face {
5
+ font-family: 'Bebasneue';
6
+ src:
7
+ url('https://cdn.jsdelivr.net/npm/bsign-customization@latest/src/fonts/BEBASNEUE-REGULAR.TTF') format('truetype');
8
+ font-weight: 400;
9
+ font-style: normal;
10
+ font-display: swap;
11
+ }
12
+
13
+ @font-face {
14
+ font-family: 'GOTHICB';
15
+ src:
16
+ url('./fonts/GOTHICB.TTF') format('truetype');
17
+ font-weight: 400;
18
+ font-style: normal;
19
+ font-display: swap;
20
+ }
21
+
22
+ @font-face {
23
+ font-family: 'Braille';
24
+ src:
25
+ url('https://cdn.jsdelivr.net/npm/bsign-customization@latest/src/fonts/Braille-Regular.ttf') format('truetype');
26
+ font-weight: 400;
27
+ font-style: normal;
28
+ font-display: swap;
29
+ }
30
+
31
+ @custom-variant dark (&:is(.dark *));
32
+
33
+ @theme inline {
34
+ --radius-sm: calc(var(--radius) - 4px);
35
+ --radius-md: calc(var(--radius) - 2px);
36
+ --radius-lg: var(--radius);
37
+ --radius-xl: calc(var(--radius) + 4px);
38
+ --color-background: var(--background);
39
+ --color-foreground: var(--foreground);
40
+ --color-card: var(--card);
41
+ --color-card-foreground: var(--card-foreground);
42
+ --color-popover: var(--popover);
43
+ --color-popover-foreground: var(--popover-foreground);
44
+ --color-primary: var(--primary);
45
+ --color-primary-foreground: var(--primary-foreground);
46
+ --color-secondary: var(--secondary);
47
+ --color-secondary-foreground: var(--secondary-foreground);
48
+ --color-muted: var(--muted);
49
+ --color-muted-foreground: var(--muted-foreground);
50
+ --color-accent: var(--accent);
51
+ --color-accent-foreground: var(--accent-foreground);
52
+ --color-destructive: var(--destructive);
53
+ --color-border: var(--border);
54
+ --color-input: var(--input);
55
+ --color-ring: var(--ring);
56
+ --color-chart-1: var(--chart-1);
57
+ --color-chart-2: var(--chart-2);
58
+ --color-chart-3: var(--chart-3);
59
+ --color-chart-4: var(--chart-4);
60
+ --color-chart-5: var(--chart-5);
61
+ --color-sidebar: var(--sidebar);
62
+ --color-sidebar-foreground: var(--sidebar-foreground);
63
+ --color-sidebar-primary: var(--sidebar-primary);
64
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
65
+ --color-sidebar-accent: var(--sidebar-accent);
66
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
67
+ --color-sidebar-border: var(--sidebar-border);
68
+ --color-sidebar-ring: var(--sidebar-ring);
69
+
70
+ --font-bebasneue: 'Bebasneue';
71
+ --font-gothicb: 'GOTHICB';
72
+ --font-braille: 'Braille';
73
+ }
74
+
75
+ :root,
76
+ :host {
77
+ --radius: 8px;
78
+ --background: oklch(1 0 0);
79
+ --foreground: oklch(0.145 0 0);
80
+ --card: oklch(1 0 0);
81
+ --card-foreground: oklch(0.145 0 0);
82
+ --popover: oklch(1 0 0);
83
+ --popover-foreground: oklch(0.145 0 0);
84
+ --primary: oklch(0.205 0 0);
85
+ --primary-foreground: oklch(0.985 0 0);
86
+ --secondary: oklch(0.97 0 0);
87
+ --secondary-foreground: oklch(0.205 0 0);
88
+ --muted: oklch(0.97 0 0);
89
+ --muted-foreground: oklch(0.556 0 0);
90
+ --accent: oklch(0.97 0 0);
91
+ --accent-foreground: oklch(0.205 0 0);
92
+ --destructive: oklch(0.577 0.245 27.325);
93
+ --border: oklch(0.922 0 0);
94
+ --input: #D6D6D6;
95
+ --ring: oklch(0.708 0 0);
96
+ --chart-1: oklch(0.646 0.222 41.116);
97
+ --chart-2: oklch(0.6 0.118 184.704);
98
+ --chart-3: oklch(0.398 0.07 227.392);
99
+ --chart-4: oklch(0.828 0.189 84.429);
100
+ --chart-5: oklch(0.769 0.188 70.08);
101
+ --sidebar: oklch(0.985 0 0);
102
+ --sidebar-foreground: oklch(0.145 0 0);
103
+ --sidebar-primary: oklch(0.205 0 0);
104
+ --sidebar-primary-foreground: oklch(0.985 0 0);
105
+ --sidebar-accent: oklch(0.97 0 0);
106
+ --sidebar-accent-foreground: oklch(0.205 0 0);
107
+ --sidebar-border: oklch(0.922 0 0);
108
+ --sidebar-ring: oklch(0.708 0 0);
109
+ }
110
+
111
+ .dark,
112
+ :host(.dark) {
113
+ --background: oklch(0.145 0 0);
114
+ --foreground: oklch(0.985 0 0);
115
+ --card: oklch(0.205 0 0);
116
+ --card-foreground: oklch(0.985 0 0);
117
+ --popover: oklch(0.205 0 0);
118
+ --popover-foreground: oklch(0.985 0 0);
119
+ --primary: oklch(0.922 0 0);
120
+ --primary-foreground: oklch(0.205 0 0);
121
+ --secondary: oklch(0.269 0 0);
122
+ --secondary-foreground: oklch(0.985 0 0);
123
+ --muted: oklch(0.269 0 0);
124
+ --muted-foreground: oklch(0.708 0 0);
125
+ --accent: oklch(0.269 0 0);
126
+ --accent-foreground: oklch(0.985 0 0);
127
+ --destructive: oklch(0.704 0.191 22.216);
128
+ --border: oklch(1 0 0 / 10%);
129
+ --input: #D6D6D6;
130
+ --ring: oklch(0.556 0 0);
131
+ --chart-1: oklch(0.488 0.243 264.376);
132
+ --chart-2: oklch(0.696 0.17 162.48);
133
+ --chart-3: oklch(0.769 0.188 70.08);
134
+ --chart-4: oklch(0.627 0.265 303.9);
135
+ --chart-5: oklch(0.645 0.246 16.439);
136
+ --sidebar: oklch(0.205 0 0);
137
+ --sidebar-foreground: oklch(0.985 0 0);
138
+ --sidebar-primary: oklch(0.488 0.243 264.376);
139
+ --sidebar-primary-foreground: oklch(0.985 0 0);
140
+ --sidebar-accent: oklch(0.269 0 0);
141
+ --sidebar-accent-foreground: oklch(0.985 0 0);
142
+ --sidebar-border: oklch(1 0 0 / 10%);
143
+ --sidebar-ring: oklch(0.556 0 0);
144
+ }
145
+
146
+ @layer base {
147
+ * {
148
+ @apply border-border outline-ring/50;
149
+ }
150
+ body,
151
+ :host {
152
+ @apply bg-background text-foreground;
153
+ }
154
+ }
155
+
156
+ input::-webkit-outer-spin-button,
157
+ input::-webkit-inner-spin-button {
158
+ -webkit-appearance: none;
159
+ margin: 0;
160
+ }
161
+
162
+ /* Firefox */
163
+ input[type=number] {
164
+ -moz-appearance: textfield;
165
+ }
166
+
167
+ .quantity-discount {
168
+ display: flex;
169
+ flex-direction: column;
170
+ gap: 8px;
171
+ padding-top: 5px;
172
+ }
173
+
174
+ .qty-discount--info, .qty-discount--item-hm {
175
+ font-size: 12px;
176
+ color: #8F8F8F;
177
+ }
178
+
179
+ .qty-discount--item-qty {
180
+ font-size: 12px;
181
+ font-weight: 500;
182
+ }
183
+
184
+ .qty-discount--item-price {
185
+ font-weight: 700;
186
+ font-size: 16px;
187
+ }
188
+
189
+ .qty-discount--item {
190
+ text-align: center;
191
+ padding: 13px 25px;
192
+ border: 1px solid #E6E6E6;
193
+ border-radius: 8px;
194
+ cursor: pointer;
195
+ position: relative;
196
+ }
197
+
198
+ .qty-discount--item-badge {
199
+ position: absolute;
200
+ text-wrap: nowrap;
201
+ top: -10px;
202
+ left: 50%;
203
+ transform: translateX(-50%);
204
+ font-size: 10px;
205
+ margin: 0;
206
+ font-weight: 600;
207
+ padding: 2px 8px;
208
+ border-radius: 4px;
209
+ }
210
+
211
+ .qty-discount--item-active {
212
+ border-color: #107E12;
213
+ }
214
+
215
+ .qty-discount--item p {
216
+ margin: 0;
217
+ }
218
+
219
+ .qty-discount--list {
220
+ display: grid;
221
+ grid-template-columns: repeat(4, 1fr);
222
+ gap: 16px;
223
+ }
224
+
225
+ @media (max-width: 768px) {
226
+ .qty-discount--list {
227
+ grid-template-columns: repeat(2, minmax(0, 1fr));
228
+ gap: 12px;
229
+ }
230
+
231
+ .qty-discount--item {
232
+ padding: 12px 10px;
233
+ }
234
+
235
+ .qty-discount--item-price {
236
+ font-size: 14px;
237
+ }
238
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,141 @@
1
+ import { createRoot, type Root } from "react-dom/client"
2
+ import SignCustomizer, { type SignCustomizerProps } from "./AppDemo2"
3
+ import resizableStyles from "react-resizable/css/styles.css?inline"
4
+ import widgetStyles from "./index.css?inline"
5
+ import { WidgetPortalContainerContext } from "./lib/widget-context"
6
+
7
+ export type WidgetTarget = string | HTMLElement
8
+
9
+ export interface MountConstructorWidgetProps extends SignCustomizerProps {
10
+ widgetCss?: string | string[]
11
+ }
12
+
13
+ const BASE_WIDGET_CSS = `${resizableStyles}\n${widgetStyles}`
14
+ const rootsByHost = new WeakMap<HTMLElement, Root>()
15
+
16
+ function resolveTarget(target: WidgetTarget): HTMLElement {
17
+ if (typeof target !== "string") {
18
+ return target
19
+ }
20
+
21
+ const element = document.querySelector(target)
22
+ if (!element) {
23
+ throw new Error(`mountConstructorWidget: target not found for selector "${target}"`)
24
+ }
25
+
26
+ if (!(element instanceof HTMLElement)) {
27
+ throw new Error(
28
+ `mountConstructorWidget: target "${target}" is not an HTMLElement`
29
+ )
30
+ }
31
+
32
+ return element
33
+ }
34
+
35
+ function ensureShadowRoot(host: HTMLElement): ShadowRoot {
36
+ return host.shadowRoot ?? host.attachShadow({ mode: "open" })
37
+ }
38
+
39
+ function ensureMountNode(shadowRoot: ShadowRoot): HTMLElement {
40
+ const existingNode = shadowRoot.querySelector<HTMLElement>("[data-bsign-widget-root]")
41
+ if (existingNode) {
42
+ return existingNode
43
+ }
44
+
45
+ const node = document.createElement("div")
46
+ node.setAttribute("data-bsign-widget-root", "")
47
+ shadowRoot.append(node)
48
+
49
+ return node
50
+ }
51
+
52
+ function ensurePortalNode(shadowRoot: ShadowRoot): HTMLElement {
53
+ const existingNode = shadowRoot.querySelector<HTMLElement>(
54
+ "[data-bsign-widget-portal-root]"
55
+ )
56
+ if (existingNode) {
57
+ return existingNode
58
+ }
59
+
60
+ const node = document.createElement("div")
61
+ node.setAttribute("data-bsign-widget-portal-root", "")
62
+ shadowRoot.append(node)
63
+
64
+ return node
65
+ }
66
+
67
+ function ensureBaseStyles(shadowRoot: ShadowRoot) {
68
+ const existingStyles = shadowRoot.querySelector<HTMLStyleElement>(
69
+ "style[data-bsign-widget-base-style]"
70
+ )
71
+ if (existingStyles) {
72
+ return
73
+ }
74
+
75
+ const styleTag = document.createElement("style")
76
+ styleTag.setAttribute("data-bsign-widget-base-style", "")
77
+ styleTag.textContent = BASE_WIDGET_CSS
78
+ shadowRoot.prepend(styleTag)
79
+ }
80
+
81
+ function normalizeWidgetCss(widgetCss?: string | string[]): string {
82
+ if (!widgetCss) {
83
+ return ""
84
+ }
85
+
86
+ const chunks = Array.isArray(widgetCss) ? widgetCss : [widgetCss]
87
+
88
+ return chunks
89
+ .filter((chunk): chunk is string => typeof chunk === "string")
90
+ .map(chunk => chunk.trim())
91
+ .filter(Boolean)
92
+ .join("\n")
93
+ }
94
+
95
+ function syncCustomStyles(shadowRoot: ShadowRoot, widgetCss?: string | string[]) {
96
+ const normalizedCss = normalizeWidgetCss(widgetCss)
97
+ const styleTag = shadowRoot.querySelector<HTMLStyleElement>(
98
+ "style[data-bsign-widget-custom-style]"
99
+ )
100
+
101
+ if (!normalizedCss) {
102
+ styleTag?.remove()
103
+ return
104
+ }
105
+
106
+ if (styleTag) {
107
+ styleTag.textContent = normalizedCss
108
+ return
109
+ }
110
+
111
+ const nextStyleTag = document.createElement("style")
112
+ nextStyleTag.setAttribute("data-bsign-widget-custom-style", "")
113
+ nextStyleTag.textContent = normalizedCss
114
+ shadowRoot.append(nextStyleTag)
115
+ }
116
+
117
+ export function mountConstructorWidget(
118
+ target: WidgetTarget,
119
+ props: MountConstructorWidgetProps = {}
120
+ ) {
121
+ const { widgetCss, ...widgetProps } = props
122
+ const host = resolveTarget(target)
123
+ const shadowRoot = ensureShadowRoot(host)
124
+ const mountNode = ensureMountNode(shadowRoot)
125
+ const portalNode = ensurePortalNode(shadowRoot)
126
+
127
+ ensureBaseStyles(shadowRoot)
128
+ syncCustomStyles(shadowRoot, widgetCss)
129
+
130
+ let root = rootsByHost.get(host)
131
+ if (!root) {
132
+ root = createRoot(mountNode)
133
+ rootsByHost.set(host, root)
134
+ }
135
+
136
+ root.render(
137
+ <WidgetPortalContainerContext.Provider value={portalNode}>
138
+ <SignCustomizer {...widgetProps} />
139
+ </WidgetPortalContainerContext.Provider>
140
+ )
141
+ }