-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathresponsiveAttributes.js
More file actions
259 lines (224 loc) · 8.13 KB
/
responsiveAttributes.js
File metadata and controls
259 lines (224 loc) · 8.13 KB
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
// Import the object that holds predefined breakpoint definitions for different frameworks
import { predefinedBreakpoints } from "./predefinedBreakpoints.js"
// Import functions for generating CSS grid rules
import { generateColumnRules } from "./helpers/generateColumnRules.js"
import { generateColumnSpanRules } from "./helpers/generateColumnSpanRules.js"
import { generateRowSpanRules } from "./helpers/generateRowSpanRules.js"
import { generateOrderRules } from "./helpers/generateOrderRules.js"
/**
* Generates CSS grid rules based on provided breakpoints.
*
* @param {Object<string, [number, string]>} breakpoints - An object defining breakpoints prefixes and their corresponding widths in pixels with an optional label for the breakpoint.
* @returns {string} The generated CSS grid rules.
*/
const createGridRules = (breakpoints) => {
// Stores all generated CSS grid rules.
let allRules = ""
// Adds copyright and root rules to the beginning of the generated CSS.
allRules += `/*
Responsive Attributes
By Matthew James Taylor
Full documentation:
https://matthewjamestaylor.com/responsive-attributes
*/
:root {
/* padding */
--pad: calc(8px + 1.5625vw);
--pad2: calc(var(--pad) * 2);
/* gutters */
--gap: calc(8px + 1.5625vw);
--gap2: calc(var(--gap) * 2);
}
`
// Iterates through each breakpoint and generates corresponding CSS grid rules.
for (const [breakpointPrefix, breakpointWidth] of Object.entries(breakpoints)) {
const rules = `/* ${breakpointWidth[1]} rules */
@media (min-width: ${breakpointWidth[0]}px) {
/* default to one column */
[data-${breakpointPrefix}*="column"] {
display: grid;
grid-template-columns: 1fr;
margin: 0;
}
/* remove leading/trailing margins on columns */
[data-${breakpointPrefix}*="column"] > * {
margin: 0;
}
/* remove leading/trailing margins on content within columns */
[data-${breakpointPrefix}*="column"] > * > :first-child {
margin-top: 0;
}
[data-${breakpointPrefix}*="column"] > * > :last-child {
margin-bottom: 0;
}
/* padding */
[data-${breakpointPrefix}~="pad"] {
padding: var(--pad);
}
[data-${breakpointPrefix}~="pad2"] {
padding: var(--pad2);
}
[data-${breakpointPrefix}] > [data-${breakpointPrefix}~="noPad"],
[data-${breakpointPrefix}~="noPad"] {
padding: 0;
}
[data-${breakpointPrefix}~="childPad"] > * {
padding: var(--pad);
}
[data-${breakpointPrefix}~="childPad2"] > * {
padding: var(--pad2);
}
/* gutter */
[data-${breakpointPrefix}~="gap"] {
gap: var(--gap);
}
[data-${breakpointPrefix}~="gap2"] {
gap: var(--gap2);
}
[data-${breakpointPrefix}] > [data-${breakpointPrefix}~="noGap"],
[data-${breakpointPrefix}~="noGap"] {
gap: 0;
}
/* columns */
${generateColumnRules(breakpointPrefix)}
/* column spans */
${generateColumnSpanRules(breakpointPrefix)}
/* row spans */
${generateRowSpanRules(breakpointPrefix)}
/* order */
${generateOrderRules(breakpointPrefix)}
/* horizontal align */
[data-${breakpointPrefix}~="left"] {
justify-self: start;
}
[data-${breakpointPrefix}~="center"] {
justify-self: center;
}
[data-${breakpointPrefix}~="right"] {
justify-self: end;
}
[data-${breakpointPrefix}~="noHA"] {
justify-self: unset;
}
/* vertical align */
[data-${breakpointPrefix}~="top"] {
align-self: start;
}
[data-${breakpointPrefix}~="middle"] {
align-self: center;
}
[data-${breakpointPrefix}~="bottom"] {
align-self: end;
}
[data-${breakpointPrefix}~="noVA"] {
align-self: unset;
}
/* text align */
[data-${breakpointPrefix}~="textL"] {
text-align: left;
}
[data-${breakpointPrefix}~="textC"] {
text-align: center;
}
[data-${breakpointPrefix}~="textR"] {
text-align: right;
}
[data-${breakpointPrefix}~="noTA"] {
text-align: unset;
}
/* hide element */
[data-${breakpointPrefix}*="hide"] {
display: none;
}
/* show element */
[data-${breakpointPrefix}*="show"] {
display: block;
}
/* responsive image */
[data-${breakpointPrefix}*="rspImg"] {
height: auto;
width: 100%;
}
}
`
// Appends the generated rules for the current breakpoint to the overall rules string.
allRules += rules
}
// Returns the final generated CSS grid rules string.
return allRules
}
/**
* Generates CSS grid rules based on provided breakpoints and prompts the user to save them as a CSS file.
*
* @param {Object<string, [number, string]>} breakpoints - An object defining breakpoints prefixes and their corresponding widths in pixels with an optional label for the breakpoint.
* @param {string} [desiredFilePath] - An optional starting directory for the save file picker. Defaults to "desktop".
* @returns {Promise<void>} A promise that resolves when the styles are successfully written to a file, or rejects if an error occurs.
*/
async function generateAndWriteStylesToFile(breakpoints, desiredFilePath) {
try {
// Generates the CSS grid rules content.
const styles = createGridRules(breakpoints)
// Creates a Blob (Binary Large Object) to represent the CSS content as a file.
const cssBlob = new Blob([styles], { type: "text/css" })
// Handle file saving based on browser compatibility:
let fileHandle
if (window.showSaveFilePicker) {
// Use File System Access API for compatible browsers
fileHandle = await window.showSaveFilePicker({
types: [{ description: "CSS files", accept: { "text/css": [".css"] } }],
startIn: desiredFilePath || "desktop", // Optional starting directory
suggestedName: "responsive-attributes.css", // Optional suggested filename
})
} else {
// Use a temporary link for Firefox
const link = document.createElement("a")
link.href = URL.createObjectURL(cssBlob)
link.download = "responsive-attributes.css"
link.click()
URL.revokeObjectURL(link.href)
return // No need to continue for Firefox
}
// Writes the CSS content to the selected file if a file handle is obtained (for File System Access API).
if (fileHandle !== null) {
const writableStream = await fileHandle.createWritable()
await writableStream.write(cssBlob)
await writableStream.close()
console.log("Styles successfully written to file!")
}
} catch (error) {
console.error("Error writing styles to file:", error)
}
}
/**
* Retrieves data based on the selection of a radio button group.
*
* @param {string} name The name attribute of the radio button group.
* @param {object} valueMap An object mapping radio button values to their corresponding data.
* @returns {any} The data associated with the selected radio button, or undefined if no selection is made.
*/
function getValueFromSelection(name, valueMap) {
// Get the selected radio button value
const selectedValue = document.querySelector(`input[name="${name}"]:checked`)?.value
// Check if a selection is made and return the corresponding value
return selectedValue ? valueMap[selectedValue] : undefined
}
/**
* Attaches a click event listener to specific HTML elements, triggering the generation and saving of CSS grid styles when clicked.
*
* @param {string} HTMLelements - The CSS selector of the HTML elements to attach the event listener to.
*/
export const createResponsiveAttributesFile = (HTMLelements) => {
// Selects the specified HTML elements.
const elements = document.querySelectorAll(HTMLelements)
// Loops through the elements.
elements.forEach((element) => {
// Adds a click event listener to the element.
element.addEventListener("click", () => {
// Retrieves the breakpoints value based on the user's selection from the radio button group named "breakpoints",
// using the predefinedBreakpoints object to map values to their corresponding breakpoints.
const breakpoints = getValueFromSelection("breakpoints", predefinedBreakpoints)
// Calls the function to generate and save CSS grid styles when the element is clicked.
generateAndWriteStylesToFile(breakpoints)
})
})
}