@eturnity/eturnity_reusable_components 7.24.2 → 7.24.3

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.
@@ -0,0 +1,517 @@
1
+ <template>
2
+ <RangeSliderContainer>
3
+ <Labels v-if="topLabels && topLabels.length > 0">
4
+ <Label v-for="label in topLabels" :key="label">
5
+ {{ label }}
6
+ </Label>
7
+ </Labels>
8
+
9
+ <BarContainer v-if="leftLabels && leftLabels.length > 0">
10
+ <BarWrapper v-for="(label, index) in leftLabels" :key="index">
11
+ <VerticalLabel>{{ label.name }}</VerticalLabel>
12
+ <BarSlider>
13
+ <Bar
14
+ @click.native.stop="
15
+ onBarRightClick({ event: $event, label, type: 'add' })
16
+ "
17
+ :data-id="`slider_bar_${dataLocation}_${label.id}`"
18
+ >
19
+ <SliderWrapper>
20
+ <Slider
21
+ v-for="(bar, index) in label.selectedTariffs"
22
+ :dataId="`slider_bar_item_${dataLocation}_${label.id}_${bar.name}`"
23
+ :key="bar.id"
24
+ :draggable="!disabled"
25
+ :resizable="!disabled"
26
+ :color="
27
+ hasOverlap && OverlapId.includes(label.id)
28
+ ? '#ff5656'
29
+ : bar.color
30
+ "
31
+ :minWidth="minWidth"
32
+ :min="bar.min"
33
+ :max="bar.max"
34
+ :z="index"
35
+ :step="step"
36
+ :stepCount="stepCount"
37
+ :subStepCount="subStepCount"
38
+ @dragStop="
39
+ onChange('drag', {
40
+ itemId: bar.id,
41
+ parentId: bar.parentId,
42
+ entityId: label.id,
43
+ ...$event
44
+ })
45
+ "
46
+ @resizeStop="
47
+ onChange('resize', {
48
+ itemId: bar.id,
49
+ entityId: label.id,
50
+ ...$event
51
+ })
52
+ "
53
+ @click.native.stop="
54
+ onBarTariffClick({ item: bar, type: 'delete', label })
55
+ "
56
+ @activated="onActivateBar({ item: bar })"
57
+ @deactivated="onDeactivateBar()"
58
+ >
59
+ <template #mr>
60
+ <HandleIcon />
61
+ </template>
62
+ <template #ml>
63
+ <HandleIcon />
64
+ </template>
65
+ </Slider>
66
+ </SliderWrapper>
67
+ </Bar>
68
+ <Ruler>
69
+ <RulerRule v-for="n in stepCount" :key="n"></RulerRule>
70
+ </Ruler>
71
+ <SubRuler>
72
+ <RulerSubRule v-for="n in subStepCount" :key="n"></RulerSubRule>
73
+ </SubRuler>
74
+ <ErrorMessage
75
+ v-if="!canOverlap && hasOverlap && OverlapId.includes(label.id)"
76
+ >
77
+ *{{ $gettext('overlap_error_message') }}
78
+ </ErrorMessage>
79
+ </BarSlider>
80
+ </BarWrapper>
81
+ </BarContainer>
82
+ <bar-options-container
83
+ v-if="showBarOptions"
84
+ :top="barOptionsPosition.y"
85
+ :left="barOptionsPosition.x"
86
+ ref="barDropdown"
87
+ >
88
+ <bar-item-container
89
+ v-for="item in barOptionsList"
90
+ :key="item.id"
91
+ @click="
92
+ onBarTariffClick({ item, type: barOptionsType, label: activeLabel })
93
+ "
94
+ >
95
+ <AddIcon />
96
+ <bar-item-text>{{ item.name }}</bar-item-text>
97
+ </bar-item-container>
98
+ </bar-options-container>
99
+ </RangeSliderContainer>
100
+ </template>
101
+
102
+ <script>
103
+ import styled from 'vue3-styled-components'
104
+ import handleSVG from '../../assets/svgIcons/handle.svg'
105
+ import add from '../../assets/svgIcons/add_icon.svg'
106
+
107
+ import SliderComponent from './Slider'
108
+
109
+ const wrapperAttrs = { width: String, height: String }
110
+ const SliderWrapper = styled('div', wrapperAttrs)`
111
+ position: relative;
112
+ display: flex;
113
+ height: ${(props) => props.height || '24px'};
114
+ width: ${(props) => props.width || '100%'};
115
+ `
116
+
117
+ const sliderAttrs = { color: String, draggable: Boolean }
118
+ const Slider = styled(SliderComponent, sliderAttrs)`
119
+ cursor: ${(props) => (props.draggable ? 'pointer' : 'not-allowed')};
120
+ opacity: 0.7;
121
+ display: flex;
122
+ justify-content: space-between;
123
+ align-items: center;
124
+ border: unset !important;
125
+ background-color: ${(props) => props.color};
126
+ `
127
+
128
+ const ErrorMessage = styled.p`
129
+ color: ${(props) => props.theme.colors.red};
130
+ font-style: italic;
131
+ font-size: 13px;
132
+ margin-top: 5px;
133
+ `
134
+
135
+ const Bar = styled.div`
136
+ display: flex;
137
+ margin-bottom: -30px;
138
+ margin-left: 1px;
139
+ background-color: #f6faff;
140
+ width: 100%;
141
+ background-color: ${(props) => props.theme.colors.gray1};
142
+ `
143
+
144
+ const Ruler = styled.div`
145
+ margin: 10px 0px -5px 0px;
146
+ display: flex;
147
+ overflow: hidden;
148
+ `
149
+
150
+ const RulerRule = styled.div`
151
+ border-left: solid 1px ${(props) => props.theme.colors.grey2};
152
+ border-bottom: solid 1px ${(props) => props.theme.colors.grey2};
153
+ display: flex;
154
+ flex-grow: 1;
155
+ flex-shrink: 1;
156
+ padding: 10px 0px;
157
+
158
+ &:last-child {
159
+ border-right: solid 1px ${(props) => props.theme.colors.grey2};
160
+ }
161
+ `
162
+
163
+ const SubRuler = styled.div`
164
+ margin: -7px 0px -5px 0px;
165
+ display: flex;
166
+ `
167
+
168
+ const RulerSubRule = styled.div`
169
+ border-left: solid 1px ${(props) => props.theme.colors.grey2};
170
+ border-bottom: solid 1px ${(props) => props.theme.colors.grey2};
171
+ display: flex;
172
+ flex-grow: 1;
173
+ flex-shrink: 1;
174
+ padding: 3px 0px;
175
+
176
+ &:last-child {
177
+ border-right: solid 1px ${(props) => props.theme.colors.grey2};
178
+ }
179
+
180
+ &:nth-child(odd) {
181
+ margin-top: -5px;
182
+ padding: 5px 0px;
183
+ }
184
+ `
185
+
186
+ const BarSlider = styled.div`
187
+ width: 100%;
188
+ `
189
+
190
+ const BarContainer = styled.div`
191
+ display: flex;
192
+ flex-direction: column;
193
+ `
194
+
195
+ const BarWrapper = styled.div`
196
+ display: flex;
197
+ flex-direction: row;
198
+ margin: 20px 0px;
199
+ align-items: center;
200
+ `
201
+
202
+ const RangeSliderContainer = styled.div`
203
+ display: flex;
204
+ position: relative;
205
+ padding: 20px 10px;
206
+ flex-direction: column;
207
+ user-select: none;
208
+ width: 100%;
209
+ `
210
+
211
+ const Labels = styled.div`
212
+ display: flex;
213
+ justify-content: space-between;
214
+ padding: 0px;
215
+ margin: 10px -100px 0px 100px;
216
+ width: calc(100% - 100px);
217
+ `
218
+
219
+ const Label = styled.div`
220
+ font-size: 80%;
221
+ display: flex;
222
+ width: 1px;
223
+ justify-content: center;
224
+ `
225
+
226
+ const VerticalLabel = styled.div`
227
+ text-align: left;
228
+ min-width: 100px;
229
+ font-size: 13px;
230
+ `
231
+
232
+ const AddIcon = styled(add)`
233
+ width: 33px;
234
+ height: 29px;
235
+ fill: black
236
+ margin-left: -17px;
237
+ margin-top: -10px;
238
+ `
239
+
240
+ const HandleIcon = styled(handleSVG)`
241
+ width: 13px;
242
+ min-width: 0;
243
+ margin: -6px;
244
+ `
245
+
246
+ const BarOptionAttrs = { top: Number, left: Number }
247
+ const BarOptionsContainer = styled('div', BarOptionAttrs)`
248
+ display: grid;
249
+ position: fixed;
250
+ z-index: 9999;
251
+ border-radius: 4px;
252
+ background-color: ${(props) => props.theme.colors.white};
253
+ padding: 6px 10px;
254
+ border: 1px solid ${(props) => props.theme.colors.grey4};
255
+ top: ${(props) => props.top + 'px'};
256
+ left: ${(props) => props.left + 'px'};
257
+ `
258
+
259
+ const BarItemContainer = styled.div`
260
+ display: grid;
261
+ grid-template-columns: auto 1fr;
262
+ grid-gap: 12px;
263
+ align-items: center;
264
+ cursor: pointer;
265
+ padding: 6px 10px;
266
+ border-radius: 4px;
267
+
268
+ &:hover {
269
+ background-color: ${(props) => props.theme.colors.lightGray};
270
+ }
271
+ `
272
+
273
+ const BarItemText = styled.div`
274
+ font-size: 13px;
275
+ `
276
+
277
+ export default {
278
+ name: 'RangeSlider',
279
+ props: {
280
+ dataLocation: { type: String, default: '' },
281
+ tariffItems: { type: Array, default: () => [] },
282
+ labels: { type: Array, default: () => [] },
283
+ rangeMargin: { type: Number, default: 1 },
284
+ step: { type: Number, default: 1 },
285
+ minWidth: { type: Number, default: 1 },
286
+ canOverlap: { type: Boolean, default: true },
287
+ disabled: { type: Boolean, default: false }
288
+ },
289
+ components: {
290
+ Bar,
291
+ Label,
292
+ Labels,
293
+ Ruler,
294
+ Slider,
295
+ AddIcon,
296
+ SubRuler,
297
+ BarSlider,
298
+ RulerRule,
299
+ HandleIcon,
300
+ BarWrapper,
301
+ ErrorMessage,
302
+ RulerSubRule,
303
+ BarContainer,
304
+ SliderWrapper,
305
+ VerticalLabel,
306
+ RangeSliderContainer,
307
+ BarOptionsContainer,
308
+ BarItemContainer,
309
+ BarItemText
310
+ },
311
+ data() {
312
+ return {
313
+ showBarOptions: false,
314
+ barOptionsPosition: { x: 0, y: 0 },
315
+ barOptionsList: [],
316
+ barOptionsType: null, // can be "add" or "delete"
317
+ activeLabel: null,
318
+ activeItem: null,
319
+ OverlapId: [],
320
+ hasOverlap: false
321
+ }
322
+ },
323
+ computed: {
324
+ maximum() {
325
+ if (!this.labels.length) return
326
+
327
+ return this.topLabels.at(-1)
328
+ },
329
+ minimum() {
330
+ if (!this.labels.length) return
331
+
332
+ return this.topLabels[0]
333
+ },
334
+ topLabels() {
335
+ if (!this.labels.length) return
336
+ const labels = this.labels.find((label) => label.placement === 'top')
337
+
338
+ return labels ? labels.value : []
339
+ },
340
+ leftLabels() {
341
+ if (!this.labels.length) return
342
+ const labels = this.labels.find((label) => label.placement === 'left')
343
+ return labels ? labels.value : []
344
+ },
345
+ stepCount() {
346
+ let labels = this.topLabels || []
347
+
348
+ if (labels.length) {
349
+ return labels.length - 1
350
+ }
351
+
352
+ return Math.floor((this.maximum - this.minimum) / this.step)
353
+ },
354
+ subStepCount() {
355
+ let labels = this.topLabels || []
356
+
357
+ if (labels.length && this.step) {
358
+ return (this.maximum - this.minimum) / this.step
359
+ }
360
+
361
+ return 0
362
+ }
363
+ },
364
+ methods: {
365
+ onChange(type, value) {
366
+ this.$emit(type, value)
367
+ },
368
+ checkOverlap(value, tariffs) {
369
+ // Check if the tariffs overlap
370
+ const min = parseFloat(value.min)
371
+ const max = parseFloat(value.max)
372
+
373
+ return tariffs.some((tariff) => {
374
+ if (tariff.id === value.itemId || tariff.id === value.id) return false
375
+
376
+ return (
377
+ min === tariff.min ||
378
+ max === tariff.max ||
379
+ (min > tariff.min && min < tariff.max) ||
380
+ (max > tariff.min && max < tariff.max) ||
381
+ (min < tariff.min && max > tariff.max)
382
+ )
383
+ })
384
+ },
385
+ barOptionLabel(value) {
386
+ if (this.barOptionsType === 'add')
387
+ return `${this.$gettext('Add')} ${value}`
388
+
389
+ return `${this.$gettext('Remove')} ${value}`
390
+ },
391
+ onActivateBar({ item }) {
392
+ this.$emit('activate')
393
+ this.activeItem = item
394
+ document.addEventListener('keydown', this.onKeyDownDelete)
395
+ },
396
+ onDeactivateBar() {
397
+ this.$emit('deactivate')
398
+ this.activeItem = null
399
+ document.removeEventListener('keydown', this.onKeyDownDelete)
400
+ },
401
+ onKeyDownDelete(event) {
402
+ // Check if the pressed key is the Delete or Backspace key
403
+ if (
404
+ (event.key === 'Delete' || event.key === 'Backspace') &&
405
+ this.activeItem
406
+ ) {
407
+ this.$emit('on-bar-tariff-click', {
408
+ type: 'delete',
409
+ item: this.activeItem,
410
+ label: this.activeLabel
411
+ })
412
+ this.activeItem = null
413
+ }
414
+ },
415
+ onBarTariffClick({ item, type, label }) {
416
+ if (this.disabled) return
417
+
418
+ this.$emit('on-bar-tariff-click', {
419
+ type,
420
+ item,
421
+ label
422
+ })
423
+
424
+ this.activeLabel = label
425
+ this.barOptionsType = type
426
+ this.showBarOptions = false
427
+ },
428
+ setBarOptions(bar) {
429
+ this.barOptionsList = []
430
+
431
+ if (this.barOptionsType === 'add') {
432
+ this.tariffItems.forEach((item) => {
433
+ if (item.name && item.name.length) {
434
+ this.barOptionsList.push(item)
435
+ }
436
+ })
437
+ } else {
438
+ // add based on the index in the chart.
439
+ this.barOptionsList.push({
440
+ name: bar.name,
441
+ id: bar.id
442
+ })
443
+ }
444
+ },
445
+ onBarRightClick({ event, label, type, bar }) {
446
+ if (this.disabled) return
447
+
448
+ // type can be "add", "delete"
449
+ // if "add", show all tariffItems for the group
450
+ // if "delete", only show the delete with the tariff name
451
+ event.preventDefault()
452
+ this.activeLabel = label
453
+ this.barOptionsType = type
454
+ this.setBarOptions(bar)
455
+
456
+ if (this.barOptionsList.length) {
457
+ this.showBarOptions = true
458
+ this.barOptionsPosition = { x: event.clientX, y: event.clientY }
459
+ document.addEventListener('click', this.handleOutsideClick)
460
+ } else {
461
+ this.showBarOptions = false
462
+ }
463
+ },
464
+ handleOutsideClick(event) {
465
+ // Check if the click occurred outside the dropdown
466
+ if (
467
+ this.$refs.barDropdown &&
468
+ !this.$refs.barDropdown.$el.contains(event.target)
469
+ ) {
470
+ // Hide the dropdown
471
+ this.showBarOptions = false
472
+ // Remove the global click event listener
473
+ document.removeEventListener('click', this.handleOutsideClick)
474
+ }
475
+ }
476
+ },
477
+ beforeDestroy() {
478
+ // Remove the global click event listener to prevent memory leaks
479
+ document.removeEventListener('click', this.handleOutsideClick)
480
+ document.removeEventListener('keydown', this.onKeyDownDelete)
481
+ },
482
+ watch: {
483
+ labels(newVal, oldVal) {
484
+ const overlapContainer = []
485
+
486
+ //check items for overlap
487
+ if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
488
+ const labels = newVal.find((label) => label.placement === 'left')
489
+
490
+ if (labels) {
491
+ labels.value.forEach((label) => {
492
+ label.selectedTariffs.forEach((tariff) => {
493
+ const hasOverlap = this.checkOverlap(
494
+ tariff,
495
+ label.selectedTariffs
496
+ )
497
+
498
+ if (hasOverlap) overlapContainer.push(label.id)
499
+ })
500
+ })
501
+ }
502
+
503
+ const hasOverlap = overlapContainer.length > 0
504
+
505
+ if (!hasOverlap) {
506
+ this.OverlapId = []
507
+ } else {
508
+ this.OverlapId = [...new Set(overlapContainer)]
509
+ }
510
+
511
+ this.hasOverlap = hasOverlap
512
+ this.$emit('has-overlap', hasOverlap)
513
+ }
514
+ }
515
+ }
516
+ }
517
+ </script>
@@ -0,0 +1,49 @@
1
+ import { isFunction } from './fns'
2
+
3
+ export function matchesSelectorToParentElements (el, selector, baseNode) {
4
+ let node = el
5
+
6
+ const matchesSelectorFunc = [
7
+ 'matches',
8
+ 'webkitMatchesSelector',
9
+ 'mozMatchesSelector',
10
+ 'msMatchesSelector',
11
+ 'oMatchesSelector'
12
+ ].find(func => isFunction(node[func]))
13
+
14
+ if (!isFunction(node[matchesSelectorFunc])) return false
15
+
16
+ do {
17
+ if (node[matchesSelectorFunc](selector)) return true
18
+ if (node === baseNode) return false
19
+ node = node.parentNode
20
+ } while (node)
21
+
22
+ return false
23
+ }
24
+
25
+ export function addEvent (el, event, handler) {
26
+ if (!el) {
27
+ return
28
+ }
29
+ if (el.attachEvent) {
30
+ el.attachEvent('on' + event, handler)
31
+ } else if (el.addEventListener) {
32
+ el.addEventListener(event, handler, true)
33
+ } else {
34
+ el['on' + event] = handler
35
+ }
36
+ }
37
+
38
+ export function removeEvent (el, event, handler) {
39
+ if (!el) {
40
+ return
41
+ }
42
+ if (el.detachEvent) {
43
+ el.detachEvent('on' + event, handler)
44
+ } else if (el.removeEventListener) {
45
+ el.removeEventListener(event, handler, true)
46
+ } else {
47
+ el['on' + event] = null
48
+ }
49
+ }
@@ -0,0 +1,26 @@
1
+ export function isFunction(func) {
2
+ return (
3
+ typeof func === 'function' ||
4
+ Object.prototype.toString.call(func) === '[object Function]'
5
+ )
6
+ }
7
+
8
+ export function snapToGrid(grid, pendingX) {
9
+ return Math.round(pendingX / grid) * grid
10
+ }
11
+
12
+ export function computeWidth(left, right) {
13
+ return right - left
14
+ }
15
+
16
+ export function restrictToBounds(value, min, max) {
17
+ if (min !== null && value < min) {
18
+ return min
19
+ }
20
+
21
+ if (max !== null && max < value) {
22
+ return max
23
+ }
24
+
25
+ return value
26
+ }
@@ -1,5 +1,9 @@
1
1
  <template>
2
- <page-container @click="toggleButton()" ref="pageContainer" :activated="isOpen">
2
+ <page-container
3
+ @click="toggleButton()"
4
+ ref="pageContainer"
5
+ :activated="isOpen"
6
+ >
3
7
  <button-container ref="dropdownItem">
4
8
  <dot-item />
5
9
  <dot-item />
@@ -44,7 +48,9 @@
44
48
  :key="item.value"
45
49
  :data-id="item.dataId"
46
50
  tabindex="0"
47
- @click.stop="onSelect({ item: item, hasChildren: hasChildren(item) })"
51
+ @click.stop="
52
+ onSelect({ item: item, hasChildren: hasChildren(item) })
53
+ "
48
54
  @keyup.enter="
49
55
  onSelect({ item: item, hasChildren: hasChildren(item) })
50
56
  "
@@ -136,7 +142,8 @@ const PageContainer = styled('div', PageContainerAttrs)`
136
142
  width: 30px;
137
143
  height: 30px;
138
144
  border-radius: 4px;
139
- background-color: ${(props) => props.activated ? props.theme.colors.grey5 : ''};
145
+ background-color: ${(props) =>
146
+ props.activated ? props.theme.colors.grey5 : ''};
140
147
 
141
148
  &:hover {
142
149
  background-color: ${(props) => props.theme.colors.grey5};
@@ -325,21 +332,28 @@ export default {
325
332
  const positionArray = this.determineElementQuarter(button, rectButton)
326
333
  contextMenu.style.transform = ''
327
334
  if (positionArray.includes('left')) {
328
- contextMenu.style.left = (rectButton.right - rectRelativeParent.left + 5) + 'px'
335
+ contextMenu.style.left =
336
+ rectButton.right - rectRelativeParent.left + 5 + 'px'
329
337
  } else {
330
- contextMenu.style.left = (rectButton.left - rectRelativeParent.left - 5) + 'px'
338
+ contextMenu.style.left =
339
+ rectButton.left - rectRelativeParent.left - 5 + 'px'
331
340
  contextMenu.style.transform = 'translateX(-100%)'
332
341
  }
333
342
  if (positionArray.includes('top')) {
334
- contextMenu.style.top = (rectButton.top - rectRelativeParent.top) + 'px'
343
+ contextMenu.style.top = rectButton.top - rectRelativeParent.top + 'px'
335
344
  } else {
336
- contextMenu.style.top = (rectButton.bottom - rectRelativeParent.top) + 'px'
345
+ contextMenu.style.top =
346
+ rectButton.bottom - rectRelativeParent.top + 'px'
337
347
  contextMenu.style.transform += ' translateY(-100%)'
338
348
  }
339
349
  },
340
350
  findRelativeParent(element) {
341
351
  while (element.parentElement) {
342
- if (window.getComputedStyle(element.parentElement).position === 'relative' || window.getComputedStyle(element.parentElement).position === 'absolute') {
352
+ if (
353
+ window.getComputedStyle(element.parentElement).position ===
354
+ 'relative' ||
355
+ window.getComputedStyle(element.parentElement).position === 'absolute'
356
+ ) {
343
357
  return element.parentElement
344
358
  }
345
359
  element = element.parentElement
@@ -0,0 +1,28 @@
1
+ export const currencyMapping = (data) => {
2
+ let currency
3
+
4
+ switch (data) {
5
+ case 'EUR':
6
+ currency = 'ct'
7
+ break
8
+ case 'CHF':
9
+ currency = 'Rp.'
10
+ break
11
+ case 'SEK':
12
+ currency = 'öre'
13
+ break
14
+ case 'GBP':
15
+ currency = 'p'
16
+ break
17
+ case 'USD':
18
+ currency = 'ct'
19
+ break
20
+ case 'DKK':
21
+ currency = 'øre'
22
+ break
23
+ default:
24
+ currency = 'ct'
25
+ }
26
+
27
+ return currency
28
+ }