jswidthbreakpoints 1.0.0 → 1.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.
@@ -15,6 +15,6 @@
15
15
  ],
16
16
  "statusbartext": {
17
17
  "active": true,
18
- "text": "🏷️ JsWidthBreakpoints v1.0.0"
18
+ "text": "🏷️ JsWidthBreakpoints v1.0.1"
19
19
  }
20
20
  }
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # JsWidthBreakpoints
2
2
 
3
- ![Image Preview](https://raw.githubusercontent.com/marceloxp/JsWidthBreakpoints/refs/heads/main/images/screen.png)
3
+ ![Image Preview](https://raw.githubusercontent.com/marceloxp/JsWidthBreakpoints/refs/heads/main/images/screen-2.png)
4
4
 
5
5
  ![Version](https://img.shields.io/github/package-json/v/marceloxp/JsWidthBreakpoints)
6
6
  ![License](https://img.shields.io/github/license/marceloxp/JsWidthBreakpoints)
package/demo/index.html CHANGED
@@ -5,24 +5,15 @@
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>JsWidthBreakpoints - Demo</title>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
8
11
  <link rel="stylesheet" href="style.css">
9
12
  <style>
10
- /* Custom styles for breakpoints */
11
- .width-lt400 .area {
12
- background-color: lightcoral;
13
- }
14
-
15
- .width-b400a600 .area {
16
- background-color: cornflowerblue;
17
- }
18
-
19
- .width-b600a800 .area {
20
- background-color: lightseagreen;
21
- }
22
-
23
- .width-gt800 .area {
24
- background-color: darkslateblue;
25
- }
13
+ .width-lt400 .area { --zone-color: #f87171; }
14
+ .width-b400a600 .area { --zone-color: #60a5fa; }
15
+ .width-b600a800 .area { --zone-color: #34d399; }
16
+ .width-gt800 .area { --zone-color: #a78bfa; }
26
17
  </style>
27
18
  </head>
28
19
 
@@ -30,16 +21,24 @@
30
21
  <div class="container">
31
22
  <div class="area">
32
23
  <h1>JsWidthBreakpoints</h1>
33
- <p>Resize the window to see the CSS classes being applied.</p>
24
+ <p class="subtitle">Resize the window to see the CSS classes being applied.</p>
34
25
  <div class="card">
35
- <h2>Breakpoints:</h2>
36
- <p id="widths">[400, 600, 800]</p>
37
- <p id="current-width">Current width: <span id="width">861</span></p>
26
+ <div class="card-row">
27
+ <span class="card-label">Breakpoints</span>
28
+ <span class="card-value mono" id="widths">[400, 600, 800]</span>
29
+ </div>
30
+ <div class="card-row">
31
+ <span class="card-label">Current width</span>
32
+ <span class="card-value mono"><span id="width">—</span>px</span>
33
+ </div>
34
+ <div class="card-row">
35
+ <span class="card-label">Active class</span>
36
+ <span class="card-value mono accent" id="active-class">—</span>
37
+ </div>
38
38
  </div>
39
39
  </div>
40
40
  </div>
41
41
 
42
- <!-- JsWidthBreakpoints Library -->
43
42
  <script src="../dist/JsWidthBreakpoints.js"></script>
44
43
  <script>
45
44
  JsWidthBreakpoints.init({
@@ -47,9 +46,9 @@
47
46
  applyClasses: true,
48
47
  classPrefix: 'width-',
49
48
  rule: {
50
- show: true, // Show the rule
51
- opacity: 1, // Opacity of the rule
52
- color: 'red', // Color of the rule
49
+ show: true,
50
+ opacity: 1,
51
+ color: 'red',
53
52
  },
54
53
  onBreakPoint: (event) => {
55
54
  console.log('Breakpoint reached:', event);
@@ -57,18 +56,11 @@
57
56
  });
58
57
 
59
58
  const drawInfo = () => {
60
- document.getElementById('current-width').innerHTML = `
61
- <p>Current width: ${window.innerWidth}
62
- <br>
63
- Current breakpoint: ${JsWidthBreakpoints.currentClass}</p>
64
- `;
59
+ document.getElementById('width').textContent = window.innerWidth;
60
+ document.getElementById('active-class').textContent = '.' + JsWidthBreakpoints.currentClass;
65
61
  };
66
62
 
67
- // Update current width display
68
- window.addEventListener('resize', (event) => {
69
- drawInfo();
70
- });
71
-
63
+ window.addEventListener('resize', drawInfo);
72
64
  drawInfo();
73
65
  </script>
74
66
  </body>
package/demo/style.css CHANGED
@@ -1,59 +1,86 @@
1
+ :root {
2
+ --zone-color: #a78bfa;
3
+ }
4
+
5
+ * { box-sizing: border-box; margin: 0; padding: 0; }
6
+
1
7
  body {
2
- margin: 0;
3
- font-family: Arial, sans-serif;
4
- background-color: #282a2b;
5
- color: #fff;
8
+ font-family: 'Inter', sans-serif;
9
+ background-color: #18181b;
10
+ color: #e4e4e7;
6
11
  display: flex;
7
12
  justify-content: center;
8
- height: 100vh;
9
- text-align: center;
13
+ min-height: 100vh;
14
+ padding: 6vh 1rem;
15
+ transition: background-color 0.5s ease;
10
16
  }
11
17
 
12
18
  .container {
13
19
  width: 80vw;
14
- max-width: 900px;
15
- min-width: 300px;
16
- margin-top: 5vh;
20
+ max-width: 520px;
21
+ min-width: 280px;
17
22
  }
18
23
 
19
24
  .area {
20
- padding: 1.5rem;
21
- border-radius: 8px;
22
- border: 1px solid #ccc;
25
+ padding: 2.5rem 2rem;
26
+ border-radius: 16px;
27
+ border: 1px solid var(--zone-color, #3f3f46);
28
+ background: color-mix(in srgb, var(--zone-color) 15%, #1c1c1f);
29
+ box-shadow: 0 0 40px -10px color-mix(in srgb, var(--zone-color) 30%, transparent);
30
+ transition: border-color 0.4s ease, box-shadow 0.4s ease, background 0.4s ease;
31
+ text-align: center;
23
32
  }
24
33
 
25
34
  h1 {
26
- font-size: clamp(1.2rem, calc(1.2rem + 2vw), 42px);
27
- margin: 0;
35
+ font-size: clamp(1.4rem, 4vw, 2rem);
36
+ font-weight: 600;
37
+ letter-spacing: -0.03em;
38
+ color: #fff;
39
+ margin-bottom: 0.5rem;
28
40
  }
29
41
 
30
- div.container > .area > p {
31
- font-size: clamp(1rem, calc(0.1rem + 1.6vw), 18px);
42
+ .subtitle {
43
+ font-size: 0.875rem;
44
+ color: #71717a;
45
+ margin-bottom: 2rem;
32
46
  }
33
47
 
34
- p {
35
- margin: 0.5rem 0;
48
+ .card {
49
+ background: #111113;
50
+ border: 1px solid #2d2d30;
51
+ border-radius: 10px;
52
+ overflow: hidden;
53
+ text-align: left;
36
54
  }
37
55
 
38
- .card {
39
- background-color: #fff;
40
- color: #333;
41
- padding: 1rem;
42
- border-radius: 8px;
43
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
44
- margin-top: 1rem;
56
+ .card-row {
57
+ display: flex;
58
+ justify-content: space-between;
59
+ align-items: center;
60
+ padding: 0.75rem 1.1rem;
61
+ border-bottom: 1px solid #2d2d30;
62
+ }
63
+
64
+ .card-row:last-child {
65
+ border-bottom: none;
66
+ }
67
+
68
+ .card-label {
69
+ font-size: 0.8rem;
70
+ color: #71717a;
45
71
  }
46
72
 
47
- .card h2 {
48
- margin: 0 0 0.5rem;
73
+ .card-value {
74
+ font-size: 0.85rem;
75
+ color: #e4e4e7;
76
+ font-weight: 500;
49
77
  }
50
78
 
51
- #widths {
52
- font-weight: bold;
53
- font-size: larger;
54
- color: deeppink;
79
+ .mono {
80
+ font-family: 'JetBrains Mono', monospace;
55
81
  }
56
82
 
57
- .card span {
58
- font-weight: bold;
83
+ .accent {
84
+ color: var(--zone-color);
85
+ transition: color 0.4s ease;
59
86
  }
@@ -0,0 +1,243 @@
1
+ /**
2
+ * JsWidthBreakpoints - A lightweight, vanilla JavaScript library for handling responsive breakpoints with dynamic CSS classes and visual rules.
3
+ * Version: 1.0.1
4
+ * Repository: https://github.com/marceloxp/JsWidthBreakpoints
5
+ * License: MIT
6
+ * Author: Marcelo XP
7
+ * Build Date: 2026-03-15
8
+ */
9
+ class JsWidthBreakpoints {
10
+ // Default settings
11
+ static defaults = {
12
+ widths: [], // Array of breakpoints in pixels (e.g., [400, 600, 800, 1200])
13
+ onBreakPoint: null, // Callback function when breakpoint changes
14
+ applyClasses: true, // Whether to apply CSS classes to <body>
15
+ classPrefix: 'width-', // Prefix for generated CSS classes
16
+ rule: {
17
+ show: false, // Whether to display the rule
18
+ opacity: 1, // Opacity of the rule
19
+ color: 'red', // Color of the rule
20
+ },
21
+ };
22
+
23
+ // Flag to prevent multiple initializations
24
+ static _isInitialized = false;
25
+
26
+ // Initialize the library
27
+ static init(options = {}) {
28
+ if (this._isInitialized) {
29
+ console.warn('JsWidthBreakpoints is already initialized. Ignoring subsequent init call.');
30
+ return;
31
+ }
32
+
33
+ // Deep merge only on rule object
34
+ const userOptions = options || {};
35
+ const mergedRule = { ...this.defaults.rule, ...(userOptions.rule || {}) };
36
+
37
+ this.options = {
38
+ ...this.defaults,
39
+ ...userOptions,
40
+ rule: mergedRule,
41
+ };
42
+
43
+ // Sort breakpoints in descending order
44
+ this.breakpoints = [...(this.options.widths || [])]
45
+ .filter(v => Number.isInteger(v) && v > 0)
46
+ .sort((a, b) => b - a);
47
+
48
+ this.breakpoints_length = this.breakpoints.length;
49
+
50
+ // Key decision: no valid breakpoints → doesn't initialize anything
51
+ if (this.breakpoints_length === 0) {
52
+ console.warn('JsWidthBreakpoints: No valid breakpoints provided. Library will not be activated.');
53
+ this._isInitialized = true;
54
+ return;
55
+ }
56
+
57
+ this.smallestBreakpoint = this.breakpoints[this.breakpoints_length - 1];
58
+ this.biggestBreakpoint = this.breakpoints[0];
59
+ this.allBreakpointClasses = this.getAllBreakpointClasses();
60
+
61
+ // Check if the callback is a function
62
+ this.hasCallback = typeof this.options.onBreakPoint === 'function';
63
+ // Get the current window width
64
+ this.currentWidth = this.getWindowWidth();
65
+ // Store the current class name
66
+ this.currentClass = null;
67
+
68
+ // Set up the window resize listener
69
+ this.setupEventListeners();
70
+
71
+ // Check and apply breakpoints immediately
72
+ this.checkBreakpoints();
73
+
74
+ // Initialize the rule if enabled
75
+ if (this.options.rule.show) {
76
+ this.injectRuleStyles();
77
+ this.createRule();
78
+ }
79
+
80
+ this._isInitialized = true;
81
+ }
82
+
83
+ // Get all possible breakpoint class names
84
+ static getAllBreakpointClasses() {
85
+ if (this.breakpoints_length === 0) return [];
86
+
87
+ const result = [
88
+ `lt${this.smallestBreakpoint}`,
89
+ `gt${this.biggestBreakpoint}`,
90
+ ];
91
+
92
+ for (let i = 1; i < this.breakpoints_length; i++) {
93
+ const lower = this.breakpoints[i];
94
+ const higher = this.breakpoints[i - 1];
95
+ result.push(`b${lower}a${higher}`);
96
+ }
97
+
98
+ return result;
99
+ }
100
+
101
+ // Get the current window width
102
+ static getWindowWidth() {
103
+ return window.innerWidth;
104
+ }
105
+
106
+ // Set up the window resize listener
107
+ static setupEventListeners() {
108
+ window.addEventListener('resize', () => this.checkBreakpoints());
109
+ }
110
+
111
+ // Main logic - simplified and callback always works
112
+ static checkBreakpoints() {
113
+ const newWidth = this.getWindowWidth();
114
+ this.currentWidth = newWidth;
115
+
116
+ const breakpoint = this.getCurrentBreakpoint();
117
+
118
+ if (breakpoint !== this.currentClass) {
119
+ const oldBreakpoint = this.currentClass || '';
120
+ this.currentClass = breakpoint;
121
+
122
+ // Execute the callback if defined
123
+ if (this.hasCallback) {
124
+ this.options.onBreakPoint({
125
+ oldBreakpoint,
126
+ currentWidth: newWidth,
127
+ currentBreakpoint: breakpoint,
128
+ });
129
+ }
130
+
131
+ // Apply classes only if enabled
132
+ if (this.options.applyClasses) {
133
+ this.applyBreakpointClasses(breakpoint);
134
+ }
135
+ }
136
+ }
137
+
138
+ static getCurrentBreakpoint() {
139
+ if (this.breakpoints_length === 0) return null;
140
+
141
+ const w = this.currentWidth;
142
+
143
+ if (w <= this.smallestBreakpoint) {
144
+ return `lt${this.smallestBreakpoint}`;
145
+ }
146
+
147
+ if (w >= this.biggestBreakpoint) {
148
+ return `gt${this.biggestBreakpoint}`;
149
+ }
150
+
151
+ for (let i = 1; i < this.breakpoints_length; i++) {
152
+ const higher = this.breakpoints[i - 1];
153
+ const lower = this.breakpoints[i];
154
+
155
+ if (w >= lower && w < higher) {
156
+ return `b${lower}a${higher}`;
157
+ }
158
+ }
159
+
160
+ return null;
161
+ }
162
+
163
+ static applyBreakpointClasses(breakpoint) {
164
+ const body = document.body;
165
+ const prefix = this.options.classPrefix;
166
+
167
+ // Remove all possible classes first
168
+ this.allBreakpointClasses.forEach((cls) => {
169
+ body.classList.remove(`${prefix}${cls}`);
170
+ });
171
+
172
+ // Add current one
173
+ if (breakpoint) {
174
+ body.classList.add(`${prefix}${breakpoint}`);
175
+ }
176
+ }
177
+
178
+ static injectRuleStyles() {
179
+ const styleId = 'jsWidthBreakpointsRuleStyles';
180
+ if (document.getElementById(styleId)) return;
181
+
182
+ const styles = `
183
+ .JsWidthBreakpoints-rule {
184
+ position: fixed;
185
+ top: 0; left: 0;
186
+ width: 100%; height: 100%;
187
+ pointer-events: none;
188
+ z-index: 1000;
189
+ }
190
+ .JsWidthBreakpoints-rule-line {
191
+ position: absolute;
192
+ top: 0; height: 100%;
193
+ width: 1px;
194
+ background-color: ${this.options.rule.color};
195
+ opacity: ${this.options.rule.opacity};
196
+ }
197
+ .JsWidthBreakpoints-rule-label {
198
+ position: absolute;
199
+ top: 10px;
200
+ left: 5px;
201
+ background-color: dimgrey;
202
+ color: white;
203
+ padding: 4px 6px;
204
+ font-size: 12px;
205
+ border-radius: 3px;
206
+ font-family: monospace;
207
+ box-shadow: 1px 1px 1px 0px rgba(0,0,0,0.75);
208
+ opacity: ${this.options.rule.opacity};
209
+ }
210
+ `;
211
+
212
+ const style = document.createElement('style');
213
+ style.id = styleId;
214
+ style.textContent = styles;
215
+ document.head.appendChild(style);
216
+ }
217
+
218
+ static createRule() {
219
+ const ruleContainer = document.createElement('div');
220
+ ruleContainer.className = 'JsWidthBreakpoints-rule';
221
+
222
+ // Lines from largest to smallest (visual order)
223
+ this.breakpoints.forEach((width) => {
224
+ const line = document.createElement('div');
225
+ line.className = 'JsWidthBreakpoints-rule-line';
226
+ line.style.left = `${width}px`;
227
+
228
+ const label = document.createElement('div');
229
+ label.className = 'JsWidthBreakpoints-rule-label';
230
+ label.textContent = `${width}px`;
231
+ label.style.left = `${width + 5}px`;
232
+
233
+ ruleContainer.appendChild(line);
234
+ ruleContainer.appendChild(label);
235
+ });
236
+
237
+ // Add the rule container to the body
238
+ document.body.appendChild(ruleContainer);
239
+ }
240
+ }
241
+
242
+ // Expose globally
243
+ window.JsWidthBreakpoints = JsWidthBreakpoints;
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * JsWidthBreakpoints - A lightweight, vanilla JavaScript library for handling responsive breakpoints with dynamic CSS classes and visual rules.
3
- * Version: 1.0.0
3
+ * Version: 1.0.1
4
4
  * Repository: https://github.com/marceloxp/JsWidthBreakpoints
5
5
  * License: MIT
6
6
  * Author: Marcelo XP
7
- * Build Date: 2025-01-21
7
+ * Build Date: 2026-03-15
8
8
  */
9
- class JsWidthBreakpoints{static defaults={widths:[],onBreakPoint:null,applyClasses:!0,classPrefix:"width-",rule:{show:!1,opacity:1,color:"red"}};static init(t={}){this.options={...this.defaults,...t},this.breakpoints=this.options.widths.sort(((t,s)=>s-t)),this.breakpoints_length=this.breakpoints.length,this.biggestBreakpoint=this.breakpoints[0],this.smallestBreakpoint=this.breakpoints[this.breakpoints_length-1],this.hasCallback="function"==typeof this.options.onBreakPoint,this.currentWidth=this.getWindowWidth(),this.allBreakpointClasses=this.getAllBreakpointClasses(),this.currentClass=null,this.setupEventListeners(),this.checkBreakpoints(!0),this.options.rule.show&&(this.injectRuleStyles(),this.createRule())}static getAllBreakpointClasses(){const t=[`lt${this.smallestBreakpoint}`,`gt${this.biggestBreakpoint}`];for(let s=this.breakpoints_length-1;s>0;s--)t.push(`b${this.breakpoints[s]}a${this.breakpoints[s-1]}`);return t}static getWindowWidth(){return window.innerWidth}static setupEventListeners(){window.addEventListener("resize",(()=>{this.checkBreakpoints()}))}static checkBreakpoints(t=!1){const s=this.getWindowWidth();if(s!==this.currentWidth||t){this.currentWidth=s;const t=this.getCurrentBreakpoint();if(this.options.applyClasses&&t!==this.currentClass){const s=this.currentClass;this.currentClass=t,this.hasCallback&&this.options.onBreakPoint({oldBreakpoint:s||"",currentWidth:this.currentWidth,currentBreakpoint:t}),this.applyBreakpointClasses(t)}}}static getCurrentBreakpoint(){if(this.currentWidth<=this.smallestBreakpoint)return`lt${this.smallestBreakpoint}`;if(this.currentWidth>=this.biggestBreakpoint)return`gt${this.biggestBreakpoint}`;for(let t=0;t<this.breakpoints_length;t++)if(this.currentWidth>=this.breakpoints[t])return`b${this.breakpoints[t]}a${this.breakpoints[t-1]}`;return null}static applyBreakpointClasses(t){const s=document.body,e=this.options.classPrefix;this.allBreakpointClasses.forEach((t=>{s.classList.remove(`${e}${t}`)})),t&&s.classList.add(`${e}${t}`)}static injectRuleStyles(){const t="jsWidthBreakpointsRuleStyles";if(document.getElementById(t))return;const s=`\n .JsWidthBreakpoints-rule {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none; /* Ensure the rule doesn't interfere with clicks */\n z-index: 1000;\n }\n\n .JsWidthBreakpoints-rule-line {\n position: absolute;\n top: 0;\n height: 100%;\n width: 1px;\n background-color: ${this.options.rule.color};\n opacity: ${this.options.rule.opacity};\n }\n\n .JsWidthBreakpoints-rule-label {\n position: absolute;\n top: 10px;\n left: 5px;\n background-color: dimgrey;\n color: white;\n padding: 4px 6px;\n font-size: 12px;\n border-radius: 3px;\n font-family: monospace;\n box-shadow: 1px 1px 1px 0px rgba(0,0,0,0.75);\n opacity: ${this.options.rule.opacity};\n }\n `,e=document.createElement("style");e.id=t,e.textContent=s,document.head.appendChild(e)}static createRule(){const t=document.createElement("div");t.className="JsWidthBreakpoints-rule",this.breakpoints.forEach((s=>{const e=document.createElement("div");e.className="JsWidthBreakpoints-rule-line",e.style.left=`${s}px`;const i=document.createElement("div");i.className="JsWidthBreakpoints-rule-label",i.textContent=`${s}px`,i.style.left=`${s+5}px`,t.appendChild(e),t.appendChild(i)})),document.body.appendChild(t)}}window.JsWidthBreakpoints=JsWidthBreakpoints;
9
+ class JsWidthBreakpoints{static defaults={widths:[],onBreakPoint:null,applyClasses:!0,classPrefix:"width-",rule:{show:!1,opacity:1,color:"red"}};static _isInitialized=!1;static init(t={}){if(this._isInitialized)return void console.warn("JsWidthBreakpoints is already initialized. Ignoring subsequent init call.");const i=t||{},s={...this.defaults.rule,...i.rule||{}};if(this.options={...this.defaults,...i,rule:s},this.breakpoints=[...this.options.widths||[]].filter((t=>Number.isInteger(t)&&t>0)).sort(((t,i)=>i-t)),this.breakpoints_length=this.breakpoints.length,0===this.breakpoints_length)return console.warn("JsWidthBreakpoints: No valid breakpoints provided. Library will not be activated."),void(this._isInitialized=!0);this.smallestBreakpoint=this.breakpoints[this.breakpoints_length-1],this.biggestBreakpoint=this.breakpoints[0],this.allBreakpointClasses=this.getAllBreakpointClasses(),this.hasCallback="function"==typeof this.options.onBreakPoint,this.currentWidth=this.getWindowWidth(),this.currentClass=null,this.setupEventListeners(),this.checkBreakpoints(),this.options.rule.show&&(this.injectRuleStyles(),this.createRule()),this._isInitialized=!0}static getAllBreakpointClasses(){if(0===this.breakpoints_length)return[];const t=[`lt${this.smallestBreakpoint}`,`gt${this.biggestBreakpoint}`];for(let i=1;i<this.breakpoints_length;i++){const s=this.breakpoints[i],e=this.breakpoints[i-1];t.push(`b${s}a${e}`)}return t}static getWindowWidth(){return window.innerWidth}static setupEventListeners(){window.addEventListener("resize",(()=>this.checkBreakpoints()))}static checkBreakpoints(){const t=this.getWindowWidth();this.currentWidth=t;const i=this.getCurrentBreakpoint();if(i!==this.currentClass){const s=this.currentClass||"";this.currentClass=i,this.hasCallback&&this.options.onBreakPoint({oldBreakpoint:s,currentWidth:t,currentBreakpoint:i}),this.options.applyClasses&&this.applyBreakpointClasses(i)}}static getCurrentBreakpoint(){if(0===this.breakpoints_length)return null;const t=this.currentWidth;if(t<=this.smallestBreakpoint)return`lt${this.smallestBreakpoint}`;if(t>=this.biggestBreakpoint)return`gt${this.biggestBreakpoint}`;for(let i=1;i<this.breakpoints_length;i++){const s=this.breakpoints[i-1],e=this.breakpoints[i];if(t>=e&&t<s)return`b${e}a${s}`}return null}static applyBreakpointClasses(t){const i=document.body,s=this.options.classPrefix;this.allBreakpointClasses.forEach((t=>{i.classList.remove(`${s}${t}`)})),t&&i.classList.add(`${s}${t}`)}static injectRuleStyles(){const t="jsWidthBreakpointsRuleStyles";if(document.getElementById(t))return;const i=`\n .JsWidthBreakpoints-rule {\n position: fixed;\n top: 0; left: 0;\n width: 100%; height: 100%;\n pointer-events: none;\n z-index: 1000;\n }\n .JsWidthBreakpoints-rule-line {\n position: absolute;\n top: 0; height: 100%;\n width: 1px;\n background-color: ${this.options.rule.color};\n opacity: ${this.options.rule.opacity};\n }\n .JsWidthBreakpoints-rule-label {\n position: absolute;\n top: 10px;\n left: 5px;\n background-color: dimgrey;\n color: white;\n padding: 4px 6px;\n font-size: 12px;\n border-radius: 3px;\n font-family: monospace;\n box-shadow: 1px 1px 1px 0px rgba(0,0,0,0.75);\n opacity: ${this.options.rule.opacity};\n }\n `,s=document.createElement("style");s.id=t,s.textContent=i,document.head.appendChild(s)}static createRule(){const t=document.createElement("div");t.className="JsWidthBreakpoints-rule",this.breakpoints.forEach((i=>{const s=document.createElement("div");s.className="JsWidthBreakpoints-rule-line",s.style.left=`${i}px`;const e=document.createElement("div");e.className="JsWidthBreakpoints-rule-label",e.textContent=`${i}px`,e.style.left=`${i+5}px`,t.appendChild(s),t.appendChild(e)})),document.body.appendChild(t)}}window.JsWidthBreakpoints=JsWidthBreakpoints;
package/gulpfile.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const gulp = require('gulp');
4
+ const merge = require('merge-stream');
4
5
  const concat = require('gulp-concat');
5
6
  const terser = require('gulp-terser');
6
7
  const header = require('gulp-header');
@@ -8,7 +9,6 @@ const rename = require('gulp-rename');
8
9
  const package = require('./package.json');
9
10
  const settingsPath = path.join(__dirname, '.vscode', 'settings.json');
10
11
 
11
- // Cabeçalho personalizado
12
12
  const banner = `/**
13
13
  * JsWidthBreakpoints - A lightweight, vanilla JavaScript library for handling responsive breakpoints with dynamic CSS classes and visual rules.
14
14
  * Version: ${package.version}
@@ -19,15 +19,22 @@ const banner = `/**
19
19
  */
20
20
  `;
21
21
 
22
- // Tarefa principal: compila e minifica o código
23
22
  function build() {
24
- return gulp
25
- .src('src/JsWidthBreakpoints.js') // Arquivo de entrada
26
- .pipe(concat('JsWidthBreakpoints.js')) // Concatena (útil se houver múltiplos arquivos)
27
- .pipe(terser()) // Minifica o código usando Terser
28
- .pipe(rename({ suffix: '.min' })) // Adiciona o sufixo .min ao nome do arquivo
29
- .pipe(header(banner)) // Adiciona o cabeçalho personalizado
30
- .pipe(gulp.dest('dist')); // Salva o arquivo minificado
23
+ const rawStream = gulp
24
+ .src('src/JsWidthBreakpoints.js')
25
+ .pipe(concat('JsWidthBreakpoints.js'))
26
+ .pipe(header(banner))
27
+ .pipe(gulp.dest('dist'));
28
+
29
+ const minStream = gulp
30
+ .src('src/JsWidthBreakpoints.js')
31
+ .pipe(concat('JsWidthBreakpoints.js'))
32
+ .pipe(terser())
33
+ .pipe(rename({ suffix: '.min' }))
34
+ .pipe(header(banner))
35
+ .pipe(gulp.dest('dist'));
36
+
37
+ return merge(rawStream, minStream);
31
38
  }
32
39
 
33
40
  async function updateStatusBar() {
@@ -45,6 +52,5 @@ async function updateStatusBar() {
45
52
  await Promise.resolve();
46
53
  }
47
54
 
48
- // Tarefa padrão: executa a tarefa de build
49
55
  exports.default = build;
50
56
  exports.updateStatusBar = updateStatusBar;
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jswidthbreakpoints",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A lightweight, vanilla JavaScript library for handling responsive breakpoints with dynamic CSS classes and visual rules.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -25,6 +25,7 @@
25
25
  "gulp-concat": "^2.6.1",
26
26
  "gulp-header": "^2.0.9",
27
27
  "gulp-rename": "^2.0.0",
28
- "gulp-terser": "^2.1.0"
28
+ "gulp-terser": "^2.1.0",
29
+ "merge-stream": "^2.0.0"
29
30
  }
30
31
  }
@@ -1,43 +1,59 @@
1
1
  class JsWidthBreakpoints {
2
2
  // Default settings
3
3
  static defaults = {
4
- widths: [], // Array of breakpoints (e.g., [400, 600, 800])
5
- onBreakPoint: null, // Callback executed when a breakpoint is reached
6
- applyClasses: true, // Dynamically apply CSS classes
7
- classPrefix: 'width-', // Prefix for CSS classes
8
- rule: { // Rule configuration
4
+ widths: [], // Array of breakpoints in pixels (e.g., [400, 600, 800, 1200])
5
+ onBreakPoint: null, // Callback function when breakpoint changes
6
+ applyClasses: true, // Whether to apply CSS classes to <body>
7
+ classPrefix: 'width-', // Prefix for generated CSS classes
8
+ rule: {
9
9
  show: false, // Whether to display the rule
10
10
  opacity: 1, // Opacity of the rule
11
11
  color: 'red', // Color of the rule
12
12
  },
13
13
  };
14
14
 
15
+ // Flag to prevent multiple initializations
16
+ static _isInitialized = false;
17
+
15
18
  // Initialize the library
16
19
  static init(options = {}) {
17
- // Merge default options with user-provided options
18
- this.options = { ...this.defaults, ...options };
20
+ if (this._isInitialized) {
21
+ console.warn('JsWidthBreakpoints is already initialized. Ignoring subsequent init call.');
22
+ return;
23
+ }
24
+
25
+ // Deep merge only on rule object
26
+ const userOptions = options || {};
27
+ const mergedRule = { ...this.defaults.rule, ...(userOptions.rule || {}) };
28
+
29
+ this.options = {
30
+ ...this.defaults,
31
+ ...userOptions,
32
+ rule: mergedRule,
33
+ };
19
34
 
20
35
  // Sort breakpoints in descending order
21
- this.breakpoints = this.options.widths.sort((a, b) => b - a);
36
+ this.breakpoints = [...(this.options.widths || [])]
37
+ .filter(v => Number.isInteger(v) && v > 0)
38
+ .sort((a, b) => b - a);
22
39
 
23
- // Store the length of the breakpoints array
24
40
  this.breakpoints_length = this.breakpoints.length;
25
41
 
26
- // Get the biggest breakpoint
27
- this.biggestBreakpoint = this.breakpoints[0];
42
+ // Key decision: no valid breakpoints → doesn't initialize anything
43
+ if (this.breakpoints_length === 0) {
44
+ console.warn('JsWidthBreakpoints: No valid breakpoints provided. Library will not be activated.');
45
+ this._isInitialized = true;
46
+ return;
47
+ }
28
48
 
29
- // Get the smallest breakpoint
30
49
  this.smallestBreakpoint = this.breakpoints[this.breakpoints_length - 1];
50
+ this.biggestBreakpoint = this.breakpoints[0];
51
+ this.allBreakpointClasses = this.getAllBreakpointClasses();
31
52
 
32
53
  // Check if the callback is a function
33
54
  this.hasCallback = typeof this.options.onBreakPoint === 'function';
34
-
35
55
  // Get the current window width
36
56
  this.currentWidth = this.getWindowWidth();
37
-
38
- // Get all possible breakpoint class names
39
- this.allBreakpointClasses = this.getAllBreakpointClasses();
40
-
41
57
  // Store the current class name
42
58
  this.currentClass = null;
43
59
 
@@ -45,24 +61,30 @@ class JsWidthBreakpoints {
45
61
  this.setupEventListeners();
46
62
 
47
63
  // Check and apply breakpoints immediately
48
- this.checkBreakpoints(true); // Pass `true` to force callback execution
64
+ this.checkBreakpoints();
49
65
 
50
66
  // Initialize the rule if enabled
51
67
  if (this.options.rule.show) {
52
- this.injectRuleStyles(); // Inject CSS styles
53
- this.createRule(); // Create the rule
68
+ this.injectRuleStyles();
69
+ this.createRule();
54
70
  }
71
+
72
+ this._isInitialized = true;
55
73
  }
56
74
 
57
- // Get all possibles breakpoint class names
75
+ // Get all possible breakpoint class names
58
76
  static getAllBreakpointClasses() {
77
+ if (this.breakpoints_length === 0) return [];
78
+
59
79
  const result = [
60
80
  `lt${this.smallestBreakpoint}`,
61
81
  `gt${this.biggestBreakpoint}`,
62
82
  ];
63
83
 
64
- for (let i = this.breakpoints_length - 1; i > 0; i--) {
65
- result.push(`b${this.breakpoints[i]}a${this.breakpoints[i - 1]}`);
84
+ for (let i = 1; i < this.breakpoints_length; i++) {
85
+ const lower = this.breakpoints[i];
86
+ const higher = this.breakpoints[i - 1];
87
+ result.push(`b${lower}a${higher}`);
66
88
  }
67
89
 
68
90
  return result;
@@ -70,105 +92,100 @@ class JsWidthBreakpoints {
70
92
 
71
93
  // Get the current window width
72
94
  static getWindowWidth() {
73
- return window.innerWidth; // Use window.innerWidth directly
95
+ return window.innerWidth;
74
96
  }
75
97
 
76
98
  // Set up the window resize listener
77
99
  static setupEventListeners() {
78
- window.addEventListener('resize', () => {
79
- this.checkBreakpoints();
80
- });
100
+ window.addEventListener('resize', () => this.checkBreakpoints());
81
101
  }
82
102
 
83
- // Check breakpoints and execute actions
84
- static checkBreakpoints(forceCallback = false) {
103
+ // Main logic - simplified and callback always works
104
+ static checkBreakpoints() {
85
105
  const newWidth = this.getWindowWidth();
106
+ this.currentWidth = newWidth;
107
+
108
+ const breakpoint = this.getCurrentBreakpoint();
86
109
 
87
- // Check if the width has changed or if the callback should be forced
88
- if (newWidth !== this.currentWidth || forceCallback) {
89
- this.currentWidth = newWidth;
90
- const breakpoint = this.getCurrentBreakpoint();
110
+ if (breakpoint !== this.currentClass) {
111
+ const oldBreakpoint = this.currentClass || '';
112
+ this.currentClass = breakpoint;
113
+
114
+ // Execute the callback if defined
115
+ if (this.hasCallback) {
116
+ this.options.onBreakPoint({
117
+ oldBreakpoint,
118
+ currentWidth: newWidth,
119
+ currentBreakpoint: breakpoint,
120
+ });
121
+ }
91
122
 
92
- // Apply CSS classes if enabled
123
+ // Apply classes only if enabled
93
124
  if (this.options.applyClasses) {
94
- // Check if the breakpoint has changed
95
- if (breakpoint !== this.currentClass) {
96
- const oldBreakpoint = this.currentClass;
97
- this.currentClass = breakpoint;
98
-
99
- // Execute the callback if defined
100
- if (this.hasCallback) {
101
- this.options.onBreakPoint({
102
- oldBreakpoint: oldBreakpoint || '',
103
- currentWidth: this.currentWidth,
104
- currentBreakpoint: breakpoint,
105
- });
106
- }
107
-
108
- // Apply CSS classes based on the breakpoint
109
- this.applyBreakpointClasses(breakpoint);
110
- }
125
+ this.applyBreakpointClasses(breakpoint);
111
126
  }
112
127
  }
113
128
  }
114
129
 
115
- // Get the current breakpoint
116
130
  static getCurrentBreakpoint() {
117
- if (this.currentWidth <= this.smallestBreakpoint) {
131
+ if (this.breakpoints_length === 0) return null;
132
+
133
+ const w = this.currentWidth;
134
+
135
+ if (w <= this.smallestBreakpoint) {
118
136
  return `lt${this.smallestBreakpoint}`;
119
- } else if (this.currentWidth >= this.biggestBreakpoint) {
137
+ }
138
+
139
+ if (w >= this.biggestBreakpoint) {
120
140
  return `gt${this.biggestBreakpoint}`;
121
- } else {
122
- for (let i = 0; i < this.breakpoints_length; i++) {
123
- if (this.currentWidth >= this.breakpoints[i]) {
124
- return `b${this.breakpoints[i]}a${this.breakpoints[i - 1]}`;
125
- }
141
+ }
142
+
143
+ for (let i = 1; i < this.breakpoints_length; i++) {
144
+ const higher = this.breakpoints[i - 1];
145
+ const lower = this.breakpoints[i];
146
+
147
+ if (w >= lower && w < higher) {
148
+ return `b${lower}a${higher}`;
126
149
  }
127
150
  }
151
+
128
152
  return null;
129
153
  }
130
154
 
131
- // Apply CSS classes based on the breakpoint
132
155
  static applyBreakpointClasses(breakpoint) {
133
156
  const body = document.body;
134
- const classPrefix = this.options.classPrefix;
157
+ const prefix = this.options.classPrefix;
135
158
 
136
- // Remove old classes
159
+ // Remove all possible classes first
137
160
  this.allBreakpointClasses.forEach((cls) => {
138
- body.classList.remove(`${classPrefix}${cls}`);
161
+ body.classList.remove(`${prefix}${cls}`);
139
162
  });
140
163
 
141
- // Add the new class
164
+ // Add current one
142
165
  if (breakpoint) {
143
- body.classList.add(`${classPrefix}${breakpoint}`);
166
+ body.classList.add(`${prefix}${breakpoint}`);
144
167
  }
145
168
  }
146
169
 
147
- // Inject CSS styles for the rule
148
170
  static injectRuleStyles() {
149
171
  const styleId = 'jsWidthBreakpointsRuleStyles';
150
- if (document.getElementById(styleId)) return; // Avoid duplicate injection
172
+ if (document.getElementById(styleId)) return;
151
173
 
152
174
  const styles = `
153
175
  .JsWidthBreakpoints-rule {
154
176
  position: fixed;
155
- top: 0;
156
- left: 0;
157
- width: 100%;
158
- height: 100%;
159
- pointer-events: none; /* Ensure the rule doesn't interfere with clicks */
177
+ top: 0; left: 0;
178
+ width: 100%; height: 100%;
179
+ pointer-events: none;
160
180
  z-index: 1000;
161
181
  }
162
-
163
182
  .JsWidthBreakpoints-rule-line {
164
183
  position: absolute;
165
- top: 0;
166
- height: 100%;
184
+ top: 0; height: 100%;
167
185
  width: 1px;
168
186
  background-color: ${this.options.rule.color};
169
187
  opacity: ${this.options.rule.opacity};
170
188
  }
171
-
172
189
  .JsWidthBreakpoints-rule-label {
173
190
  position: absolute;
174
191
  top: 10px;
@@ -184,18 +201,17 @@ class JsWidthBreakpoints {
184
201
  }
185
202
  `;
186
203
 
187
- const styleElement = document.createElement('style');
188
- styleElement.id = styleId;
189
- styleElement.textContent = styles;
190
- document.head.appendChild(styleElement);
204
+ const style = document.createElement('style');
205
+ style.id = styleId;
206
+ style.textContent = styles;
207
+ document.head.appendChild(style);
191
208
  }
192
209
 
193
- // Create the rule (régua)
194
210
  static createRule() {
195
211
  const ruleContainer = document.createElement('div');
196
212
  ruleContainer.className = 'JsWidthBreakpoints-rule';
197
213
 
198
- // Add lines for each breakpoint
214
+ // Lines from largest to smallest (visual order)
199
215
  this.breakpoints.forEach((width) => {
200
216
  const line = document.createElement('div');
201
217
  line.className = 'JsWidthBreakpoints-rule-line';
@@ -204,7 +220,7 @@ class JsWidthBreakpoints {
204
220
  const label = document.createElement('div');
205
221
  label.className = 'JsWidthBreakpoints-rule-label';
206
222
  label.textContent = `${width}px`;
207
- label.style.left = `${width + 5}px`; // Offset the label slightly
223
+ label.style.left = `${width + 5}px`;
208
224
 
209
225
  ruleContainer.appendChild(line);
210
226
  ruleContainer.appendChild(label);
@@ -215,5 +231,5 @@ class JsWidthBreakpoints {
215
231
  }
216
232
  }
217
233
 
218
- // Expose the class globally
234
+ // Expose globally
219
235
  window.JsWidthBreakpoints = JsWidthBreakpoints;