wave-ui 1.49.3 → 1.51.0

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wave-ui",
3
- "version": "1.49.3",
4
- "description": "An emerging UI framework for Vue.js & Vue 3 with only the bright side. :sunny:",
3
+ "version": "1.51.0",
4
+ "description": "An emerging UI framework for Vue.js (2 & 3) with only the bright side. :sunny:",
5
5
  "author": "Antoni Andre <antoniandre.web@gmail.com>",
6
6
  "main": "./dist/wave-ui.umd.js",
7
7
  "unpkg": "dist/wave-ui.umd.js",
@@ -16,6 +16,7 @@ export { default as WDrawer } from './w-drawer.vue'
16
16
  export { default as WFlex } from './w-flex.vue'
17
17
  export { default as WForm } from './w-form.vue'
18
18
  export { default as WFormElement } from './w-form-element.vue'
19
+ export { default as WGrid } from './w-grid.vue'
19
20
  export { default as WIcon } from './w-icon.vue'
20
21
  export { default as WImage } from './w-image.vue'
21
22
  export { default as WInput } from './w-input.vue'
@@ -4,12 +4,13 @@ w-overlay.w-dialog(
4
4
  :persistent="persistent"
5
5
  :persistent-no-animation="persistentNoAnimation"
6
6
  @click="onOutsideClick"
7
+ @closed="$emit('closed')"
7
8
  :bg-color="overlayColor"
8
9
  :opacity="overlayOpacity"
9
10
  :class="classes")
10
11
  transition(:name="transition" appear @after-leave="onClose")
11
12
  w-card.w-dialog__content(
12
- v-if="showContent"
13
+ v-show="showContent"
13
14
  no-border
14
15
  :color="color"
15
16
  :bg-color="bgColor"
@@ -18,10 +19,10 @@ w-overlay.w-dialog(
18
19
  :content-class="contentClass"
19
20
  :title="title || undefined"
20
21
  :style="contentStyles")
21
- template(v-if="$slots.title" v-slot:title)
22
+ template(#title v-if="$slots.title")
22
23
  slot(name="title")
23
24
  slot
24
- template(v-if="$slots.actions" v-slot:actions)
25
+ template(#actions v-if="$slots.actions")
25
26
  slot(name="actions")
26
27
  </template>
27
28
 
@@ -47,7 +48,7 @@ export default {
47
48
  overlayOpacity: { type: [Number, String, Boolean] }
48
49
  },
49
50
 
50
- emits: ['input', 'update:modelValue', 'close'],
51
+ emits: ['input', 'update:modelValue', 'close', 'closed'],
51
52
 
52
53
  data () {
53
54
  return {
@@ -87,7 +88,7 @@ export default {
87
88
  this.showWrapper = false
88
89
  this.$emit('update:modelValue', false)
89
90
  this.$emit('input', false)
90
- this.$emit('close', false)
91
+ this.$emit('close')
91
92
  }
92
93
  },
93
94
 
@@ -1,8 +1,5 @@
1
1
  <template lang="pug">
2
- component.w-flex-wrap(v-if="gap" :is="tag")
3
- .w-flex(:class="classes")
4
- slot
5
- component.w-flex(v-else :class="classes" :is="tag")
2
+ component.w-flex(:class="classes" :is="tag")
6
3
  slot
7
4
  </template>
8
5
 
@@ -28,7 +25,7 @@ export default {
28
25
  justifySpaceAround: { type: Boolean },
29
26
  justifySpaceEvenly: { type: Boolean },
30
27
  basisZero: { type: Boolean },
31
- gap: { type: Number, default: 0 }
28
+ gap: { type: [Number, String], default: 0 }
32
29
  },
33
30
 
34
31
  computed: {
@@ -51,7 +48,7 @@ export default {
51
48
  'justify-space-around': this.justifySpaceAround,
52
49
  'justify-space-evenly': this.justifySpaceEvenly,
53
50
  'basis-zero': this.basisZero,
54
- [`w-flex--gap${this.gap}`]: this.gap
51
+ [`gap${this.gap}`]: ~~this.gap
55
52
  }
56
53
  }
57
54
  }
@@ -68,12 +65,5 @@ export default {
68
65
  &.wrap {flex-wrap: wrap;}
69
66
  &.basis-zero > * {flex-basis: 0;flex-grow: 1;}
70
67
  &.basis-zero > .no-grow, &.basis-zero > .shrink {flex-grow: 0;}
71
-
72
- @for $i from 1 through 12 {
73
- // Divide by 2 as there are 2 elements having this space.
74
- $space: round($base-increment * divide($i, 2));
75
- &--gap#{$i} {margin: -$space;}
76
- &--gap#{$i} > * {margin: $space;}
77
- }
78
68
  }
79
69
  </style>
@@ -0,0 +1,82 @@
1
+ <template lang="pug">
2
+ component.w-grid(:class="classes" :is="tag")
3
+ slot
4
+ </template>
5
+
6
+ <script>
7
+ export default {
8
+ name: 'w-grid',
9
+ props: {
10
+ tag: { type: String, default: 'div' },
11
+ columns: { type: [Number, Object, String] },
12
+ gap: { type: [Number, Object, String], default: 0 }
13
+ },
14
+
15
+ computed: {
16
+ breakpointsColumns () {
17
+ let columns = { xs: 0, sm: 0, md: 0, lg: 0, xl: 0 }
18
+ switch (typeof this.columns) {
19
+ case 'object':
20
+ columns = Object.assign(columns, this.columns)
21
+ break
22
+ case 'number':
23
+ case 'string':
24
+ columns = Object.keys(columns).reduce((obj, breakpoint) => (obj[breakpoint] = ~~this.columns), {})
25
+ break
26
+ default:
27
+ break
28
+ }
29
+ return columns
30
+ },
31
+
32
+ breakpointsGap () {
33
+ let gap = { xs: 0, sm: 0, md: 0, lg: 0, xl: 0 }
34
+ switch (typeof this.gap) {
35
+ case 'object':
36
+ gap = Object.assign(gap, this.gap)
37
+ break
38
+ case 'number':
39
+ case 'string':
40
+ gap = Object.keys(gap).reduce((obj, breakpoint) => (obj[breakpoint] = ~~this.gap), {})
41
+ break
42
+ default:
43
+ break
44
+ }
45
+ return gap
46
+ },
47
+
48
+ classes () {
49
+ let breakpointsColumns = null
50
+ if (typeof this.columns === 'object') {
51
+ breakpointsColumns = Object.entries(this.breakpointsColumns).reduce((obj, [breakpoint, columns]) => {
52
+ obj[`${breakpoint}-columns${columns}`] = true
53
+ return obj
54
+ }, {})
55
+ }
56
+
57
+ let breakpointsGap = null
58
+ if (typeof this.gap === 'object') {
59
+ breakpointsGap = Object.entries(this.breakpointsGap).reduce((obj, [breakpoint, gap]) => {
60
+ obj[`${breakpoint}-gap${gap}`] = true
61
+ return obj
62
+ }, {})
63
+ }
64
+
65
+ return {
66
+ ...(breakpointsColumns || { [`columns${this.columns}`]: this.columns }),
67
+ ...(breakpointsGap || { [`gap${this.gap}`]: this.gap })
68
+ }
69
+ }
70
+ }
71
+ }
72
+ </script>
73
+
74
+ <style lang="scss">
75
+ .w-grid {
76
+ display: grid;
77
+
78
+ @for $i from 1 through 12 {
79
+ &.columns#{$i} {grid-template-columns: repeat($i, 1fr);}
80
+ }
81
+ }
82
+ </style>
@@ -5,6 +5,7 @@
5
5
  .w-menu(
6
6
  v-if="custom && detachableVisible"
7
7
  ref="detachable"
8
+ v-on="$listeners"
8
9
  @click="hideOnMenuClick && close(true)"
9
10
  @mouseenter="showOnHover && (hoveringMenu = true)"
10
11
  @mouseleave="showOnHover && ((hoveringMenu = false), close())"
@@ -14,6 +15,7 @@
14
15
  w-card.w-menu(
15
16
  v-else-if="detachableVisible"
16
17
  ref="detachable"
18
+ v-on="$listeners"
17
19
  @click.native="hideOnMenuClick && close(true)"
18
20
  @mouseenter.native="showOnHover && (hoveringMenu = true)"
19
21
  @mouseleave.native="showOnHover && ((hoveringMenu = false), close())"
@@ -134,7 +136,10 @@ export default {
134
136
  },
135
137
 
136
138
  overlayClasses () {
137
- return objectifyClasses(this.overlayClass)
139
+ return {
140
+ ...objectifyClasses(this.overlayClass),
141
+ 'w-overlay--no-pointer-event': this.showOnHover
142
+ }
138
143
  },
139
144
 
140
145
  classes () {
@@ -196,6 +201,7 @@ export default {
196
201
  methods: {
197
202
  /**
198
203
  * Other methods in the `detachable` mixin:
204
+ * - `open`
199
205
  * - `getActivatorCoordinates`
200
206
  * - `computeDetachableCoords`
201
207
  * - `onResize`
@@ -207,7 +213,8 @@ export default {
207
213
  // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
208
214
  toggle (e) {
209
215
  let shouldShowMenu = this.detachableVisible
210
- if ('ontouchstart' in window && this.showOnHover && e.type === 'click') {
216
+ if (typeof window !== 'undefined' && 'ontouchstart' in window &&
217
+ this.showOnHover && e.type === 'click') {
211
218
  shouldShowMenu = !shouldShowMenu
212
219
  }
213
220
  else if (e.type === 'click' && !this.showOnHover) shouldShowMenu = !shouldShowMenu
@@ -226,37 +233,6 @@ export default {
226
233
  else this.close()
227
234
  },
228
235
 
229
- // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
230
- async open (e) {
231
- // A tiny delay may help positioning the detachable correctly in case of multiple activators
232
- // with different menu contents.
233
- if (this.delay) await new Promise(resolve => setTimeout(resolve, this.delay))
234
-
235
- this.detachableVisible = true
236
-
237
- // If the activator is external, there might be multiple,
238
- // so on open, the activator will be set to the event target.
239
- if (this.activator) this.activatorEl = e.target
240
-
241
- await this.insertInDOM()
242
-
243
- if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
244
-
245
- if (!this.noPosition) this.computeDetachableCoords(e)
246
-
247
- // In `getActivatorCoordinates` accessing the menu computed styles takes a few ms (less than 10ms),
248
- // if we don't postpone the Menu apparition it will start transition from a visible menu and
249
- // thus will not transition.
250
- this.timeoutId = setTimeout(() => {
251
- this.$emit('update:modelValue', true)
252
- this.$emit('input', true)
253
- this.$emit('open')
254
- }, 0)
255
-
256
- if (!this.persistent) document.addEventListener('mousedown', this.onOutsideMousedown)
257
- if (!this.noPosition) window.addEventListener('resize', this.onResize)
258
- },
259
-
260
236
  /**
261
237
  * Closes the menu. Can happen on:
262
238
  * - click of activator
@@ -1,5 +1,5 @@
1
1
  <template lang="pug">
2
- transition(name="fade" mode="out-in" appear)
2
+ transition(name="fade" appear @after-leave="$emit('closed')")
3
3
  .w-overlay(
4
4
  v-if="value"
5
5
  :style="(value && styles) || null"
@@ -24,7 +24,7 @@ export default {
24
24
  persistentNoAnimation: { type: Boolean }
25
25
  },
26
26
 
27
- emits: ['input', 'update:modelValue', 'click', 'close'],
27
+ emits: ['input', 'update:modelValue', 'click', 'close', 'closed'],
28
28
 
29
29
  data: () => ({
30
30
  persistentAnimate: false
@@ -60,7 +60,7 @@ export default {
60
60
  else if (!this.persistent) {
61
61
  this.$emit('update:modelValue', false)
62
62
  this.$emit('input', false)
63
- this.$emit('close', false)
63
+ this.$emit('close')
64
64
  }
65
65
 
66
66
  this.$emit('click', e)
@@ -85,6 +85,7 @@ export default {
85
85
  background-color: rgba(0, 0, 0, 0.3);
86
86
 
87
87
  &--persistent-animate {animation: 0.15s w-overlay-pop cubic-bezier(0.6, -0.28, 0.74, 0.05);}
88
+ &--no-pointer-event {pointer-events: none;}
88
89
  }
89
90
 
90
91
  @keyframes w-overlay-pop {
@@ -25,11 +25,12 @@ component(
25
25
  align-left
26
26
  custom
27
27
  min-width="activator"
28
+ @mousedown="isFocused = true, selectingItem = true"
29
+ @mouseup="isFocused = true, selectingItem = false"
28
30
  v-bind="menuProps || {}")
29
31
  template(#activator="{ on }")
30
32
  //- Input wrapper.
31
33
  .w-select__selection-wrap(
32
- ref="selection-wrap"
33
34
  @click="!isDisabled && !isReadonly && (showMenu ? closeMenu : openMenu)()"
34
35
  role="button"
35
36
  aria-haspopup="listbox"
@@ -173,7 +174,9 @@ export default {
173
174
  inputValue: [],
174
175
  showMenu: false,
175
176
  menuMinWidth: 0,
176
- isFocused: false
177
+ isFocused: false,
178
+ selectingItem: false,
179
+ selectionWrapRef: undefined
177
180
  }),
178
181
 
179
182
  computed: {
@@ -210,7 +213,7 @@ export default {
210
213
  'w-select--disabled': this.isDisabled,
211
214
  'w-select--readonly': this.isReadonly,
212
215
  [`w-select--${this.hasValue ? 'filled' : 'empty'}`]: true,
213
- 'w-select--focused': this.isFocused && !this.isReadonly,
216
+ 'w-select--focused': (this.isFocused || this.selectingItem) && !this.isReadonly,
214
217
  'w-select--dark': this.dark,
215
218
  'w-select--floating-label': this.hasLabel && this.labelPosition === 'inside' && !this.staticLabel,
216
219
  'w-select--no-padding': !this.outline && !this.bgColor && !this.shadow && !this.round,
@@ -129,6 +129,7 @@ export default {
129
129
  methods: {
130
130
  /**
131
131
  * Other methods in the `detachable` mixin:
132
+ * - `open`
132
133
  * - `getActivatorCoordinates`
133
134
  * - `computeDetachableCoords`
134
135
  * - `onResize`
@@ -152,37 +153,6 @@ export default {
152
153
  else this.close()
153
154
  },
154
155
 
155
- // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
156
- async open (e) {
157
- // A tiny delay may help positioning the detachable correctly in case of multiple activators
158
- // with different menu contents.
159
- if (this.delay) await new Promise(resolve => setTimeout(resolve, this.delay))
160
-
161
- this.detachableVisible = true
162
-
163
- // If the activator is external, there might be multiple,
164
- // so on open, the activator will be set to the event target.
165
- if (this.activator) this.activatorEl = e.target
166
-
167
- await this.insertInDOM()
168
-
169
- if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
170
-
171
- if (!this.noPosition) this.computeDetachableCoords(e)
172
-
173
- // In `getActivatorCoordinates` accessing the tooltip computed styles takes a few ms (less than 10ms),
174
- // if we don't postpone the Tooltip apparition it will start transition from a visible tooltip and
175
- // thus will not transition.
176
- this.timeoutId = setTimeout(() => {
177
- this.$emit('update:modelValue', true)
178
- this.$emit('input', true)
179
- this.$emit('open')
180
- }, 0)
181
-
182
- if (!this.persistent) document.addEventListener('mousedown', this.onOutsideMousedown)
183
- if (!this.noPosition) window.addEventListener('resize', this.onResize)
184
- },
185
-
186
156
  /**
187
157
  * Closes the tooltip. Can happen on:
188
158
  * - click of activator
@@ -22,7 +22,8 @@ export default {
22
22
  alignRight: { type: Boolean },
23
23
  noPosition: { type: Boolean },
24
24
  zIndex: { type: [Number, String, Boolean] },
25
- activator: { type: String } // Optionally designate an external activator.
25
+ // Optionally designate an external activator.
26
+ activator: { type: [String, Object, HTMLElement] } // The activator can be a DOM string selector, a ref or a DOM node.
26
27
  },
27
28
 
28
29
  data: () => ({
@@ -31,7 +32,7 @@ export default {
31
32
  // as is in an array so we can delete them on destroy.
32
33
  // This only applies to the activatorEventHandlers, the other events listeners can be removed
33
34
  // normally.
34
- docAEventListenersHandlers: []
35
+ docEventListenersHandlers: []
35
36
  }),
36
37
 
37
38
  computed: {
@@ -70,17 +71,22 @@ export default {
70
71
  },
71
72
 
72
73
  hasSeparateActivator () {
73
- return !this.$scopedSlots.activator && typeof this.activator === 'string'
74
+ if (this.$scopedSlots.activator) return false
75
+ const activatorIsString = typeof this.activator === 'string'
76
+ const activatorIsDomEl = (this.activator?.$el || this.activator) instanceof HTMLElement
77
+ return activatorIsString || activatorIsDomEl
74
78
  },
75
79
 
76
80
  activatorEl: {
77
81
  get () {
78
- if (this.hasSeparateActivator) return document.querySelector(this.activator)
82
+ if (this.hasSeparateActivator) {
83
+ const activator = this.activator?.$el || this.activator
84
+ if (activator instanceof HTMLElement) return activator
85
+ return document.querySelector(this.activator)
86
+ }
79
87
  return this.$el.firstElementChild
80
88
  },
81
- set () {
82
-
83
- }
89
+ set () {}
84
90
  },
85
91
 
86
92
  position () {
@@ -106,9 +112,40 @@ export default {
106
112
 
107
113
  methods: {
108
114
  // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
109
- getActivatorCoordinates (e) {
115
+ async open (e) {
116
+ // A tiny delay may help positioning the detachable correctly in case of multiple activators
117
+ // with different menu contents.
118
+ if (this.delay) await new Promise(resolve => setTimeout(resolve, this.delay))
119
+
120
+ this.detachableVisible = true
121
+
122
+ // If the activator is external, there might be multiple,
123
+ // so on open, the activator will be set to the event target.
124
+ if (this.activator) this.activatorEl = e.target
125
+
126
+ await this.insertInDOM()
127
+
128
+ if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
129
+
130
+ if (!this.noPosition) this.computeDetachableCoords()
131
+
132
+ // In `getActivatorCoordinates` accessing the menu computed styles takes a few ms (less than 10ms),
133
+ // if we don't postpone the Menu apparition it will start transition from a visible menu and
134
+ // thus will not transition.
135
+ this.timeoutId = setTimeout(() => {
136
+ this.$emit('update:modelValue', true)
137
+ this.$emit('input', true)
138
+ this.$emit('open')
139
+ }, 0)
140
+
141
+ if (!this.persistent) document.addEventListener('mousedown', this.onOutsideMousedown)
142
+ if (!this.noPosition) window.addEventListener('resize', this.onResize)
143
+ },
144
+
145
+ // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
146
+ getActivatorCoordinates () {
110
147
  // Get the activator coordinates relative to window.
111
- const { top, left, width, height } = (e ? e.target : this.activatorEl).getBoundingClientRect()
148
+ const { top, left, width, height } = (this.activatorEl).getBoundingClientRect()
112
149
  let coords = { top, left, width, height }
113
150
 
114
151
  // If absolute position, adjust top & left.
@@ -126,9 +163,9 @@ export default {
126
163
  },
127
164
 
128
165
  // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
129
- computeDetachableCoords (e) {
166
+ computeDetachableCoords () {
130
167
  // Get the activator coordinates.
131
- let { top, left, width, height } = this.getActivatorCoordinates(e)
168
+ let { top, left, width, height } = this.getActivatorCoordinates()
132
169
 
133
170
  // 1. First display the menu but hide it (So we can get its dimension).
134
171
  // --------------------------------------------------
@@ -251,31 +288,47 @@ export default {
251
288
  this.detachableEl.remove()
252
289
  this.detachableEl = null
253
290
  }
254
- }
255
- },
256
-
257
- mounted () {
258
- const wrapper = this.$el
259
-
260
- // Unwrap the activator element if the activator is in the activator slot.
261
- if (this.$scopedSlots.activator) wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
291
+ },
262
292
 
263
293
  // If the activator is external, add event listeners to the document and check the target is
264
294
  // the activator when toggling.
265
295
  // This way, the activator can be a future DOM element, that is not yet in the DOM.
266
- else if (this.activator) {
296
+ bindActivatorEvents () {
297
+ const activatorIsString = typeof this.activator === 'string'
298
+
267
299
  Object.entries(this.activatorEventHandlers).forEach(([eventName, handler]) => {
268
300
  // Convert mouseenter to mouseover & mouseleave to mouseout because we are attaching
269
- // event to the document, so it can accept future nodes.
301
+ // event to the document, so it can accept future DOM nodes.
270
302
  eventName = eventName.replace('mouseenter', 'mouseover').replace('mouseleave', 'mouseout')
271
303
  const handlerWrap = e => {
272
- if (e.target?.matches && e.target.matches(this.activator)) handler(e)
304
+ // The activator can be a DOM string selector a ref or a DOM node.
305
+ if (activatorIsString && e.target?.matches && e.target.matches(this.activator)) handler(e)
306
+ else if (e.target === this.activatorEl || this.activatorEl.contains(e.target)) handler(e)
273
307
  }
274
308
  document.addEventListener(eventName, handlerWrap)
275
309
  // The event listeners handlers have to be removed the exact same way they have been attached.
276
310
  // Since the handler functions have variables that change after hot-reload, keep them exactly
277
311
  // as is in an array so we can delete them on destroy.
278
- this.docAEventListenersHandlers.push({ eventName, handler: handlerWrap })
312
+ this.docEventListenersHandlers.push({ eventName, handler: handlerWrap })
313
+ })
314
+ }
315
+ },
316
+
317
+ mounted () {
318
+ const wrapper = this.$el
319
+
320
+ // Unwrap the activator element if the activator is in the activator slot.
321
+ if (this.$scopedSlots.activator) wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
322
+
323
+ // If the activator is external.
324
+ else if (this.activator) this.bindActivatorEvents()
325
+
326
+ // If the activator seems to be undefined, it is probably a DOM node or Vue ref,
327
+ // so check it on nextTick.
328
+ else {
329
+ this.$nextTick(() => {
330
+ this.activator && this.bindActivatorEvents()
331
+ if (this.value) this.toggle({ type: 'click', target: this.activatorEl })
279
332
  })
280
333
  }
281
334
 
@@ -285,18 +338,21 @@ export default {
285
338
  wrapper.parentNode.insertBefore(this.overlayEl, wrapper)
286
339
  }
287
340
 
288
- if (this.value) this.toggleMenu({ type: 'click', target: this.activatorEl })
341
+ if (this.value && this.activator) this.toggle({ type: 'click', target: this.activatorEl })
289
342
  },
290
343
 
291
- beforeDestroy () {
344
+ // Must be on destroy and not before destroy, so that when used in a dialog, we wait for the end
345
+ // of the animation before removing the activator element.
346
+ // https://github.com/antoniandre/wave-ui/issues/82
347
+ destroy () {
292
348
  this.close()
293
349
 
294
350
  this.removeFromDOM()
295
351
 
296
352
  // Remove the event listeners the exact same way they have been defined.
297
353
  // Fixes issues on hot-reloading.
298
- if (this.docAEventListenersHandlers.length) {
299
- this.docAEventListenersHandlers.forEach(({ eventName, handler }) => {
354
+ if (this.docEventListenersHandlers.length) {
355
+ this.docEventListenersHandlers.forEach(({ eventName, handler }) => {
300
356
  document.removeEventListener(eventName, handler)
301
357
  })
302
358
  }
@@ -212,3 +212,8 @@
212
212
  // ----------------------------------------------
213
213
  }
214
214
  }
215
+
216
+ @for $i from 1 through 12 {
217
+ .w-flex.gap#{$i}, .w-grid.gap#{$i} {gap: round($base-increment * $i);}
218
+ }
219
+ .w-flex.gap0, .w-grid.gap0 {gap: 0;}
@@ -150,7 +150,7 @@ const generateBreakpointSpaces = () => {
150
150
 
151
151
  const genBreakpointLayoutClasses = () => {
152
152
  let styles = ''
153
- const { cssScope } = cssVars
153
+ const { cssScope, baseIncrement } = cssVars
154
154
  const layoutClasses = [
155
155
  'show{display:block}',
156
156
  'hide{display:none}',
@@ -185,6 +185,7 @@ const genBreakpointLayoutClasses = () => {
185
185
  'justify-space-around{justify-content:space-around}',
186
186
  'justify-space-evenly{justify-content:space-evenly}'
187
187
  ]
188
+ const array12 = Array(12).fill()
188
189
 
189
190
  // Define media queries for each breakpoint: xs, sm, md, lg, xl.
190
191
 
@@ -194,6 +195,10 @@ const genBreakpointLayoutClasses = () => {
194
195
  styles +=
195
196
  `@media(min-width:${min}px){` +
196
197
  layoutClasses.map(rule => `${cssScope} .${label}u-${rule}`).join('') +
198
+ // w-grid columns and gap.
199
+ array12.map((item, i) => `.w-grid.${label}u-columns${i + 1}{grid-template-columns:repeat(${i + 1},1fr);}`).join('') +
200
+ array12.map((item, i) => `.w-flex.${label}u-gap${i + 1},.w-grid.${label}u-gap${i + 1}{gap:${(i + 1) * baseIncrement}px;}`).join('') +
201
+ `.w-flex.${label}u-gap0,.w-flex.${label}u-gap0{gap:0}` +
197
202
  '}'
198
203
  }
199
204
  })
@@ -202,6 +207,10 @@ const genBreakpointLayoutClasses = () => {
202
207
  styles +=
203
208
  `@media (min-width:${min}px) and (max-width:${max}px){` +
204
209
  layoutClasses.map(rule => `${cssScope} .${label}-${rule}`).join('') +
210
+ // w-grid columns and gap.
211
+ array12.map((item, i) => `.w-grid.${label}-columns${i + 1}{grid-template-columns:repeat(${i + 1},1fr);}`).join('') +
212
+ array12.map((item, i) => `.w-flex.${label}-gap${i + 1},.w-grid.${label}-gap${i + 1}{gap:${(i + 1) * baseIncrement}px;}`).join('') +
213
+ `.w-flex.${label}-gap0,.w-flex.${label}-gap0{gap:0}` +
205
214
  '}'
206
215
  })
207
216
 
@@ -210,6 +219,10 @@ const genBreakpointLayoutClasses = () => {
210
219
  styles +=
211
220
  `@media (max-width:${max}px){` +
212
221
  layoutClasses.map(rule => `${cssScope} .${label}d-${rule}`).join('') +
222
+ // w-grid columns and gap.
223
+ array12.map((item, i) => `.w-grid.${label}d-columns${i + 1}{grid-template-columns:repeat(${i + 1},1fr);}`).join('') +
224
+ array12.map((item, i) => `.w-flex.${label}d-gap${i + 1},.w-grid.${label}d-gap${i + 1}{gap:${(i + 1) * baseIncrement}px;}`).join('') +
225
+ `.w-flex.${label}d-gap0,.w-flex.${label}d-gap0{gap:0}` +
213
226
  '}'
214
227
  }
215
228
  })