-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgridtoggler-0.2.0.js
286 lines (268 loc) · 10.3 KB
/
gridtoggler-0.2.0.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
// @ts-check;
;
/**
* The wrapper anonymous function, being triggered by window 'load' event.
*/
window.addEventListener('load', () => {
/**
* The core configuration object.
* @param {string} targetX Valid DOM selector targeting horizontal grid container.
* @param {string} asstargetY Valid DOM selector targeting vertical grid container.
* @param {number} gridGapX Distance between horizontal grid guides (px).
* @param {number} gridGapY Distance between vertical grid guides (px).
* @param {number} gridTiltX Horizontal grid offset (px).
* @param {number} gridTiltY Vertical grid offset (px).
* @param {string} gridColor Valid CSS color for guides.
*/
const config = window.localStorage && JSON.parse(localStorage.getItem("gridTogglerConfig")) || {
targetX: '#grid',
targetY: 'body',
gridGapX: 30,
gridGapY: 30,
gridTiltX: 0,
gridTiltY: 0,
gridColor: 'rgb(50, 154, 240)',
};
const queryX = document.querySelectorAll(config.targetX);
const queryY = document.querySelectorAll(config.targetY);
/**
* Returns an array of static CSS rules to be applied on butons and form.
* @return {array} A set of static CSS rules.
*/
const staticStyles = () => {
return [
`.gt {
background: transparent;
display: inline-block;
position: fixed;
right: 2rem;
top: 1rem;
z-index: 9999;
}`,
`.gt__btn-group {
box-shadow: 2px 2px 3px 0px rgba(0, 0, 0, .3);
display: inline-block;
margin-bottom: 1rem;
}`,
`.gt__btn {
display: inline-block;
padding: 1rem 2rem 1rem 2rem;
position: relative;
border:1px solid #aeaeae;
border-right: none;
background: linear-gradient(0deg, #e1e1e1, #fefefe);
line-height: 28px;
cursor: pointer;
}`,
`.gt__btn:focus {
outline: none;
}`,
`.gt__btn:last-child {
border-right: 1px solid #aeaeae;
}`,
`.gt__btn::before {
background-repeat: no-repeat;
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
width: 15px;
height: 15px;
}`,
`.gt__btn--x::before {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' width='15px' height='15px' viewBox='0 0 15 15' enable-background='new 0 0 15 15'%3E%3Cpath d='M13 0H14V15H13zM9 0H10V15H9zM5 0H6V15H5zM1 0H2V15H1z'/%3E%3C/svg%3E");
}`,
`.gt__btn--y::before {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' width='15px' height='15px' viewBox='0 0 15 15' enable-background='new 0 0 15 15'%3E%3Cpath d='M0 1H15V2H0zM0 5H15V6H0zM0 9H15V10H0zM0 13H15V14H0z'/%3E%3C/svg%3E");
}`,
`.gt__btn--menu::before {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='15px' height='15px' viewBox='-2.491 0 15 15' enable-background='new -2.491 0 15 15' xml:space='preserve'%3E%3Ccircle cx='5' cy='2.5' r='1.5'/%3E%3Ccircle cx='5' cy='7.5' r='1.5'/%3E%3Ccircle cx='5' cy='12.5' r='1.5'/%3E%3C/svg%3E");
}`,
`.gt__btn--active {
background: linear-gradient(0deg, #cbcbcb, #cecece);
box-shadow: 0 0 10px rgba(0, 0, 0, .1) inset;
}`,
`.gt__form {
background: #fafafa;
border:1px solid #aeaeae;
box-shadow: 2px 2px 3px 0px rgba(0, 0, 0, .3);
display: none;
padding: 0.5rem;
}`,
`.gt__form--active {
display: block;
}`,
`.gt__label {
font-family: sans-serif;
font-size: 0.75rem;
}`,
`.gt__input-num {
display: block;
margin-bottom: 0.5rem;
margin-top: 0.25rem;
max-width: 3rem;
}`,
];
};
/**
* Returns an array of dynamic CSS rules to be applied on grid target elements.
* @return {array} A set of dynamic CSS rules.
*/
const dynamicStyles = () => {
return [
`.gridX--active {
background-image: repeating-linear-gradient(
90deg,
transparent,
transparent ${config.gridGapX - 1}px,
${config.gridColor} ${config.gridGapX - 1}px,
${config.gridColor} ${config.gridGapX}px
);
background-position-x: ${config.gridTiltX}px;
}`,
`.gridY--active {
background-image: repeating-linear-gradient(
0deg,
transparent,
transparent ${config.gridGapY - 1}px,
${config.gridColor} ${config.gridGapY - 1}px,
${config.gridColor} ${config.gridGapY}px
);
background-position-y: ${config.gridTiltY}px;
}`,
`.gridX--active.gridY--active {
background-image: repeating-linear-gradient(
90deg,
transparent,
transparent ${config.gridGapX - 1}px,
${config.gridColor} ${config.gridGapX - 1}px,
${config.gridColor} ${config.gridGapX}px
),
repeating-linear-gradient(
0deg,
transparent,
transparent ${config.gridGapY - 1}px,
${config.gridColor} ${config.gridGapY - 1}px,
${config.gridColor} ${config.gridGapY}px
)
}`,
];
};
/**
* Creates a </style> element in documents' head and appends given rules to it.
* @param {function} source – A function which returns an array of CSS rules.
* @param {string} type – A 'data-gt-type' attribute value allowing to query that stylesheet.
* @return {object} A single stylesheet.
*/
const appendStyles = (source, type) => {
const style = document.createElement('style');
document.head.appendChild(style);
style.setAttribute('data-gt-type', type);
source().forEach((rule, index) => {
style.sheet.insertRule(rule, index);
});
return style.sheet;
};
appendStyles(staticStyles, 'static');
appendStyles(dynamicStyles, 'dynamic');
const dynamicSheet = document.head.querySelector("[data-gt-type='dynamic']").sheet;
/**
* Toggles classes on given targets.
* @param {object} query – A HTML Collection of elements targeted via config object.
* @param {string} className – A name of CSS class to be toggled.
* @param {object} e – An event object.
* @return {function} An IIFE to toggle CSS classes of elements passed in the 'query' param.
*/
const toggleGrid = (query, className, e) => {
e.target.classList.toggle('gt__btn--active');
if (!query.length) {
console.warn(
`We are sorry, but the toggleGrid script couldn't find any elements with given attributes.
Please, check the config object on top of "gridtoggler.js" file.
If you think this is a bug, please report.`,
);
return false;
}
return (() => {
[...query].forEach((elm) => {
elm.classList.toggle(className);
});
})();
};
/**
* Calls the toggleGrid() function with specific params.
* @param {object} e – An event object.
*/
const toggleXGrid = (e) => {
toggleGrid(queryX, 'gridX--active', e);
};
/**
* Calls the toggleGrid() function with specific params.
* @param {object} e – An event object.
*/
const toggleYGrid = (e) => {
toggleGrid(queryY, 'gridY--active', e);
};
const toggleMenu = (e) => {
e.target.classList.toggle('gt__btn--active');
document.getElementById('gt__form').classList.toggle('gt__form--active');
}
/**
* Deletes all rules in a stylesheet being queried by const 'dynamicSheet'.
* Then insert modified rules.
*/
const refreshDynamicStyles = () => {
const len = dynamicSheet.cssRules.length;
for (let i = 0; i < len; i += 1) {
dynamicSheet.deleteRule(0);
}
dynamicStyles().forEach((rule, index) => {
dynamicSheet.insertRule(rule, index);
});
};
/**
* Changes the value of given config object property.
* Either the key and the value are passed in the event object.
* @param {object} e – An event object.
*/
const changeValue = (name, value) => {
config[name] = Number(value);
refreshDynamicStyles();
};
const saveConfig = () => {
localStorage.setItem("gridTogglerConfig", JSON.stringify(config));
}
const domString = `<div class="gt">
<div class="gt__btn-group">
<button id="gt__Xbtn" class="gt__btn gt__btn--x" />
<button id="gt__Ybtn" class="gt__btn gt__btn--y" />
<button id="gt__toggleMenu" class="gt__btn gt__btn--menu" />
</div>
<div style="display: block"></div>
<form id="gt__form" class="gt__form" class="gt__settings">
<label class="gt__label" for="gridGapX">grig gap X</label>
<input class="gt__input-num" type="number" min="2" name="gridGapX" value=${config.gridGapX} />
<label class="gt__label" for="gridGapY">grid gap Y</label>
<input class="gt__input-num" type="number" min="2" name="gridGapY" value=${config.gridGapY} />
<label class="gt__label" for="gridTiltX">grit tilt X</label>
<input class="gt__input-num" type="number" name="gridTiltX" value=${config.gridTiltX} />
<label class="gt__label" for="gridTiltY">grid tilt Y</label>
<input class="gt__input-num" type="number" name="gridTiltY" value=${config.gridTiltY} />
${window.localStorage && `<input id="gt__save" type="button" value="Save config" />`}
</form>
</div>`;
document.body.insertAdjacentHTML('afterbegin', domString);
document.getElementById('gt__Xbtn').addEventListener('click', (e) => toggleXGrid(e), false);
document.getElementById('gt__Ybtn').addEventListener('click', (e) => toggleYGrid(e), false);
document.getElementById('gt__toggleMenu').addEventListener('click', (e) => toggleMenu(e), false);
document.getElementById('gt__save').addEventListener('click', saveConfig, false);
[...document.getElementsByClassName('gt__input-num')].forEach((elem) => {
elem.addEventListener('change', (e) => {
let { name, value } = e.target;
return changeValue(name, value)
}, false);
});
}, false);