Skip to content

Commit 29bdb9a

Browse files
committed
feat: attribute-target can now target a container in same document or in another document. creating websitebuilders or richtexteditors just became a lot simpler
1 parent 5b3097c commit 29bdb9a

3 files changed

Lines changed: 125 additions & 29 deletions

File tree

demo/iframe.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<body>
1919

2020
<h1 id="new">canvas;#new</h1>
21+
<h1 >canvas;</h1>
2122

2223
<!--<script src="https://cdn.cocreate.app/attributes/latest/CoCreate-attributes.min.js"></script>-->
2324
<script src="../../../CoCreateJS/dist/CoCreate.js"></script>

demo/index.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616

1717

1818
<!-- CoCreate SideNav Right -->
19-
<sidenav id="menuR" class="position:fixed top:0px right:0px direction:rtl overflow:hidden background:gainsboro height:100vh width:300px" resizable resize-target="[content_id='content']" resize-property="margin-right" resize-value="width">
19+
<sidenav id="menuR" class="position:fixed top:0px right:0px overflow:hidden background:gainsboro height:100vh width:300px" resizable resize-target="[content_id='content']" resize-property="margin-right" resize-value="width">
2020
<h1 id="new" class="background:green">testing</h1>
2121
<div class="padding:5px height:100vh overflow:auto">
22-
<form>
22+
<form class="width:250px">
2323

24-
<input attribute="myAttribute" type="text" class="floating-label attr" placeholder="Draggable" attribute-target="#new">
24+
<input attribute="myAttribute" type="text" class="floating-label attr" placeholder="Draggable" attribute-target="iframe; *">
25+
<input attribute="myAttribute" type="text" class="floating-label attr" placeholder="Draggable" attribute-target="iframe; *">
2526
<input id="margin-left" class="floating-label z-index:0 text_color" placeholder="margin-left" attribute="classstyle" attribute-property="margin-left" attribute-target="#new" active>
2627

2728
<input class="floating-label z-index:0 text_color" placeholder="background" attribute="style" attribute-property="background" attribute-target="#new" active>

src/index.js

Lines changed: 120 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@ from './common.js';
1616

1717
import observer from '@cocreate/observer';
1818
import crdt from '@cocreate/crdt';
19+
import uuid from '@cocreate/uuid';
1920
// import message from '@cocreate/message-client';
2021
import action from '@cocreate/action';
2122
import pickr from '@cocreate/pickr';
22-
// import {cssPath} from '@cocreate/utils';
23+
import {cssPath} from '@cocreate/utils';
24+
2325
import { containerSelector as ccSelectSelector } from '@cocreate/select/src/config';
2426

2527
let cache = new elStore();
2628

2729
let initDocument = document;
30+
let containers = new Map();
2831

2932
function init() {
3033
let inputs = document.querySelectorAll(`[attribute-target]`);
@@ -42,20 +45,96 @@ async function initElement(input, el) {
4245
if (input.hasAttribute('actions'))
4346
return;
4447
// let value = getInputValue(input);
45-
let { element, type, property, camelProperty } = await parseInput(input, el);
46-
if(!element) return;
47-
updateInput({ input, element, type, property, camelProperty, isColl: true });
48-
49-
// ToDo: if input has a value updateElement, need to be catious with observer target update input may have previousvalue
50-
if(!el && value) {
51-
updateElement({ input, element, type, property, camelProperty, isColl: true });
48+
49+
let selector = input.getAttribute("attribute-target");
50+
if(selector.indexOf('*') !== -1) {
51+
let sel = selector.replace('*', '')
52+
addClickEvent(input, sel)
53+
}
54+
else{
55+
let { element, type, property, camelProperty } = await parseInput(input, el);
56+
if(!element) return;
57+
updateInput({ input, element, type, property, camelProperty, isColl: true });
58+
59+
// ToDo: if input has a value updateElement, need to be catious with observer target update input may have previousvalue
60+
if(!el && value) {
61+
updateElement({ input, element, type, property, camelProperty, isColl: true });
62+
}
5263
}
5364
}
5465
catch(err) {
5566

5667
}
5768
}
5869

70+
function addClickEvent(input, selector) {
71+
let container;
72+
let Document;
73+
if(selector.indexOf(';') !== -1) {
74+
let [frameSelector, target] = selector.split(';');
75+
let frame = document.querySelector(frameSelector);
76+
Document = frame.contentDocument;
77+
if (target && target != ' ' && frame){
78+
container = Document.querySelector(target)
79+
}
80+
else if (frame){
81+
container = Document
82+
}
83+
}
84+
else
85+
container = document.querySelector(selector)
86+
87+
if(!containers.has(container)){
88+
let inputs = new Map()
89+
container.addEventListener('click', elClicked)
90+
containers.set(container, inputs)
91+
}
92+
containers.get(container).set(input, '')
93+
}
94+
95+
async function elClicked(e) {
96+
let inputs = containers.get(e.currentTarget);
97+
for (let [input] of inputs) {
98+
input.targetElement = e.target;
99+
input.targetContainer = e.currentTarget;
100+
let eid = e.target.getAttribute('eid');
101+
102+
if(e.target.id){
103+
eid = e.target.id
104+
}
105+
else if (!eid) {
106+
eid = uuid.generate(6);
107+
let domTextEditor;
108+
if(e.currentTarget.nodeName == '#document') {
109+
let documentElement = e.currentTarget.documentElement;
110+
if (documentElement.hasAttribute('contenteditable'))
111+
domTextEditor = documentElement;
112+
}
113+
else if (e.currentTarget.hasAttribute('contenteditable'))
114+
domTextEditor = e.currentTarget
115+
if (domTextEditor)
116+
CoCreate.text.setAttribute({ domTextEditor, target: e.target, name: 'eid', value: eid });
117+
}
118+
119+
input.value = '';
120+
121+
let attribute;
122+
if (input.id)
123+
attribute = input.id
124+
else
125+
attribute = input.getAttribute('attribute-property')
126+
if (!attribute)
127+
attribute = input.getAttribute('attribute')
128+
129+
input.setAttribute('name', attribute + '-' + eid);
130+
e.target.setAttribute('eid', eid);
131+
input.targetPath = cssPath(e.target)
132+
let { element, type, property, camelProperty } = await parseInput(input, e.target);
133+
if(!element) return;
134+
updateInput({ input, element, type, property, camelProperty, isColl: true });
135+
}
136+
}
137+
59138
async function parseInput(input, element) {
60139
if(!element) {
61140
let selector = input.getAttribute("attribute-target");
@@ -97,7 +176,12 @@ function initEvents() {
97176

98177
async function inputEvent(e) {
99178
let input = e.target;
100-
let { element, type, property, camelProperty } = await parseInput(input);
179+
let container = input.targetContainer;
180+
let path = input.targetPath;
181+
let el;
182+
if (container && path)
183+
el = container.querySelector(path);
184+
let { element, type, property, camelProperty } = await parseInput(input, el);
101185
updateElement({ input, element, type, property, camelProperty, isColl: true });
102186
}
103187

@@ -150,15 +234,13 @@ function observerElements(initWindow) {
150234

151235
function getInputFromElement(element, attribute) {
152236
let inputs = initDocument.querySelectorAll(`[attribute="${attribute}"]`);
237+
let matching = [];
153238
for (let input of inputs){
154239
let selector = input.getAttribute('attribute-target');
155-
if (selector && !element.matches(selector))
156-
inputs.shift();
157-
}
158-
if(inputs) {
159-
return inputs;
240+
if (selector && element.matches(selector))
241+
matching.push(input);
160242
}
161-
return [];
243+
return matching;
162244
}
163245

164246
function removeZeros(str) {
@@ -172,11 +254,14 @@ function removeZeros(str) {
172254

173255
async function updateElement({ input, element, collValue, isColl, unit, type, property, camelProperty, ...rest }) {
174256
if (!element) {
175-
let parsed = await parseInput(input);
176-
element = parsed.element;
177-
type = parsed.type;
178-
property = parsed.property;
179-
camelProperty = parsed.camelProperty;
257+
let e = {target: input};
258+
inputEvent(e);
259+
return;
260+
// let parsed = await parseInput(input);
261+
// element = parsed.element;
262+
// type = parsed.type;
263+
// property = parsed.property;
264+
// camelProperty = parsed.camelProperty;
180265
}
181266
let inputValue = collValue != undefined ? collValue : getInputValue(input);
182267
if(!inputValue) return;
@@ -477,14 +562,14 @@ function getInputValue(input) {
477562
forceState: true
478563
});
479564

480-
case 'pickr':
481-
// todo: how to perform validation
482-
// if (!CoCreate.pickr.refs.has(input)) return;
483-
let pickrIns = pickr.refs.get(input);
484-
return pickrIns ? pickrIns.getColor() : '';
565+
// case 'pickr':
566+
// // todo: how to perform validation
567+
// // if (!CoCreate.pickr.refs.has(input)) return;
568+
// let pickrIns = pickr.refs.get(input);
569+
// return pickrIns ? pickrIns.getColor() : '';
485570

486571
default:
487-
let value = input.getAttribute('value');
572+
let value = input.value || input.getAttribute('value');
488573
if (value) return value;
489574
return false;
490575
}
@@ -512,6 +597,15 @@ function getRealStaticCompStyle(element) {
512597

513598
init();
514599

600+
observer.init({
601+
name: "ccAttribute",
602+
observe: ["childList"],
603+
target: '[attribute]',
604+
callback: function(mutation) {
605+
initElements(mutation.addedNodes);
606+
}
607+
});
608+
515609
observer.init({
516610
name: "ccAttribute",
517611
observe: ["attributes"],

0 commit comments

Comments
 (0)