@aprovan/bobbin 0.1.0-dev.03aaf5b
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/.turbo/turbo-build.log +16 -0
- package/LICENSE +373 -0
- package/dist/index.d.ts +402 -0
- package/dist/index.js +3704 -0
- package/package.json +30 -0
- package/src/Bobbin.tsx +89 -0
- package/src/components/EditPanel/EditPanel.tsx +376 -0
- package/src/components/EditPanel/controls/ColorPicker.tsx +138 -0
- package/src/components/EditPanel/controls/QuickSelectDropdown.tsx +142 -0
- package/src/components/EditPanel/controls/SliderInput.tsx +94 -0
- package/src/components/EditPanel/controls/SpacingControl.tsx +285 -0
- package/src/components/EditPanel/controls/ToggleGroup.tsx +37 -0
- package/src/components/EditPanel/controls/TokenDropdown.tsx +33 -0
- package/src/components/EditPanel/sections/AnnotationSection.tsx +136 -0
- package/src/components/EditPanel/sections/BackgroundSection.tsx +79 -0
- package/src/components/EditPanel/sections/EffectsSection.tsx +85 -0
- package/src/components/EditPanel/sections/LayoutSection.tsx +224 -0
- package/src/components/EditPanel/sections/SectionWrapper.tsx +57 -0
- package/src/components/EditPanel/sections/SizeSection.tsx +166 -0
- package/src/components/EditPanel/sections/SpacingSection.tsx +69 -0
- package/src/components/EditPanel/sections/TypographySection.tsx +148 -0
- package/src/components/Inspector/Inspector.tsx +221 -0
- package/src/components/Overlay/ControlHandles.tsx +572 -0
- package/src/components/Overlay/MarginPaddingOverlay.tsx +229 -0
- package/src/components/Overlay/SelectionOverlay.tsx +73 -0
- package/src/components/Pill/Pill.tsx +155 -0
- package/src/components/ThemeToggle/ThemeToggle.tsx +72 -0
- package/src/core/changeSerializer.ts +139 -0
- package/src/core/useBobbin.ts +399 -0
- package/src/core/useChangeTracker.ts +186 -0
- package/src/core/useClipboard.ts +21 -0
- package/src/core/useElementSelection.ts +146 -0
- package/src/index.ts +46 -0
- package/src/tokens/borders.ts +19 -0
- package/src/tokens/colors.ts +150 -0
- package/src/tokens/index.ts +37 -0
- package/src/tokens/shadows.ts +10 -0
- package/src/tokens/spacing.ts +37 -0
- package/src/tokens/typography.ts +51 -0
- package/src/types.ts +157 -0
- package/src/utils/animation.ts +40 -0
- package/src/utils/dom.ts +36 -0
- package/src/utils/selectors.ts +76 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +10 -0
package/src/utils/dom.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function applyStyleToElement(
|
|
2
|
+
el: HTMLElement,
|
|
3
|
+
property: string,
|
|
4
|
+
value: string,
|
|
5
|
+
): void {
|
|
6
|
+
el.style.setProperty(property, value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function enableContentEditable(el: HTMLElement, enabled: boolean): void {
|
|
10
|
+
if (enabled) {
|
|
11
|
+
el.contentEditable = 'true';
|
|
12
|
+
// Recursively enable for text nodes
|
|
13
|
+
const textElements = el.querySelectorAll(
|
|
14
|
+
'p, span, h1, h2, h3, h4, h5, h6, a, li, td, th, label, div',
|
|
15
|
+
);
|
|
16
|
+
textElements.forEach((child) => {
|
|
17
|
+
if (child instanceof HTMLElement && child.childNodes.length > 0) {
|
|
18
|
+
// Only make editable if it has direct text content
|
|
19
|
+
for (const node of child.childNodes) {
|
|
20
|
+
if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) {
|
|
21
|
+
child.contentEditable = 'true';
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
} else {
|
|
28
|
+
el.contentEditable = 'false';
|
|
29
|
+
const textElements = el.querySelectorAll('[contenteditable="true"]');
|
|
30
|
+
textElements.forEach((child) => {
|
|
31
|
+
if (child instanceof HTMLElement) {
|
|
32
|
+
child.contentEditable = 'false';
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export function generateId(): string {
|
|
2
|
+
return Math.random().toString(36).substring(2, 15);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function getElementPath(el: HTMLElement): string {
|
|
6
|
+
const path: string[] = [];
|
|
7
|
+
let current: HTMLElement | null = el;
|
|
8
|
+
|
|
9
|
+
while (current && current !== document.body) {
|
|
10
|
+
let selector = current.tagName.toLowerCase();
|
|
11
|
+
|
|
12
|
+
if (current.id) {
|
|
13
|
+
selector = `#${current.id}`;
|
|
14
|
+
path.unshift(selector);
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Add nth-child for uniqueness
|
|
19
|
+
const parent = current.parentElement;
|
|
20
|
+
if (parent) {
|
|
21
|
+
const siblings = Array.from(parent.children).filter(
|
|
22
|
+
(c) => c.tagName === current!.tagName,
|
|
23
|
+
);
|
|
24
|
+
if (siblings.length > 1) {
|
|
25
|
+
const index = siblings.indexOf(current) + 1;
|
|
26
|
+
selector += `:nth-of-type(${index})`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
path.unshift(selector);
|
|
31
|
+
current = current.parentElement;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return path.join(' > ');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getElementXPath(el: HTMLElement): string {
|
|
38
|
+
const parts: string[] = [];
|
|
39
|
+
let current: HTMLElement | null = el;
|
|
40
|
+
|
|
41
|
+
while (
|
|
42
|
+
current &&
|
|
43
|
+
current !== document.body &&
|
|
44
|
+
current !== document.documentElement
|
|
45
|
+
) {
|
|
46
|
+
let part = current.tagName.toLowerCase();
|
|
47
|
+
|
|
48
|
+
// If element has an id, use it as anchor
|
|
49
|
+
if (current.id) {
|
|
50
|
+
parts.unshift(`//*[@id="${current.id}"]`);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Calculate position among siblings of same tag
|
|
55
|
+
const parent = current.parentElement;
|
|
56
|
+
if (parent) {
|
|
57
|
+
const siblings = Array.from(parent.children).filter(
|
|
58
|
+
(c) => c.tagName === current!.tagName,
|
|
59
|
+
);
|
|
60
|
+
if (siblings.length > 1) {
|
|
61
|
+
const index = siblings.indexOf(current) + 1;
|
|
62
|
+
part += `[${index}]`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
parts.unshift(part);
|
|
67
|
+
current = current.parentElement;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// If we didn't find an id anchor, start from root
|
|
71
|
+
if (!parts[0]?.startsWith('//*[@id')) {
|
|
72
|
+
parts.unshift('');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return parts.join('/') || '//' + el.tagName.toLowerCase();
|
|
76
|
+
}
|
package/tsconfig.json
ADDED