@teammodlr/ui-kit 2.1.73 → 2.1.75

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.
@@ -25,7 +25,7 @@
25
25
  <template v-else>
26
26
  <div :class="style.content">
27
27
  <button role="button" :class="style.close" @click="close"></button>
28
- <div :class="style.header" ref="handle">
28
+ <div :class="headerClasses" ref="handle">
29
29
  <div :class="style.title">
30
30
  <slot name="title">
31
31
  {{ resolvedTitle }}
@@ -50,7 +50,7 @@
50
50
  </Teleport>
51
51
  </template>
52
52
  <script lang="ts">
53
- import {fade, modalScale} from '../../styles/transitions.css';
53
+ import { fade, modalScale } from '../../styles/transitions.css';
54
54
  import {
55
55
  computed,
56
56
  defineComponent,
@@ -58,12 +58,13 @@ import {
58
58
  watch,
59
59
  nextTick,
60
60
  isRef,
61
- type CSSProperties, type PropType, type Ref,
61
+ type CSSProperties,
62
+ type PropType,
63
+ type Ref,
62
64
  } from 'vue';
63
- import type {ComputedRef} from 'vue';
64
-
65
- import {useZIndex} from '../../composables/useZIndex';
66
- import {useDraggable} from '@vueuse/core';
65
+ import type { ComputedRef } from 'vue';
66
+ import { useZIndex } from '../../composables/useZIndex';
67
+ import { useDraggable, useEventListener } from '@vueuse/core';
67
68
  import * as style from './style';
68
69
 
69
70
  export default defineComponent({
@@ -83,8 +84,7 @@ export default defineComponent({
83
84
  },
84
85
  onClose: {
85
86
  type: Function,
86
- default: () => {
87
- },
87
+ default: () => {},
88
88
  },
89
89
  zIndex: {
90
90
  type: Number,
@@ -102,22 +102,25 @@ export default defineComponent({
102
102
  default: false,
103
103
  },
104
104
  },
105
- emits: ['onOpen', 'onClose', 'update:open'],
106
- setup(props, {emit, expose}) {
105
+ // Eventually remove 'open', i lazy cant be assed for now
106
+ emits: ['onOpen', 'onClose', 'update:open', 'open'],
107
+ setup(props, { emit, expose }) {
107
108
  const wrapper = ref<HTMLElement | null>(null);
108
109
  const dialog = ref<HTMLElement | null>(null);
109
110
  const handle = ref<HTMLElement | null>(null);
110
- const {nextZIndex} = useZIndex();
111
+ const { nextZIndex } = useZIndex();
111
112
  const zIndex = ref(props.zIndex ?? nextZIndex());
112
113
 
113
114
  const isOpen = ref<boolean>(props.open);
115
+ const isDragging = ref<boolean>(false);
114
116
 
115
- const {x, y} = useDraggable(dialog, {
117
+ const { x, y } = useDraggable(dialog, {
116
118
  handle,
117
119
  preventDefault: true,
118
120
  containerElement: wrapper,
119
121
  disabled: computed(() => !props.draggable),
120
122
  onStart: onDragStart,
123
+ onEnd: onDragStop
121
124
  });
122
125
 
123
126
  const resolvedTitle = computed(() =>
@@ -129,13 +132,19 @@ export default defineComponent({
129
132
  [style.backdropColor]: props.modal === true,
130
133
  }));
131
134
 
135
+ const headerClasses = computed(() => ({
136
+ [style.header]: true,
137
+ [style.draggable]: props.draggable && !isDragging.value,
138
+ [style.dragging]: props.draggable && isDragging.value
139
+ }));
140
+
132
141
  const wrapperClasses = computed(() => ({
133
142
  [style.wrapper]: true,
134
143
  [style.notModal]: !props.modal,
135
144
  }));
136
145
 
137
146
  const width = computed(() =>
138
- typeof props.width === 'number' ? `${props.width}px` : props.width,
147
+ typeof props.width === 'number' ? `${props.width}px` : props.width
139
148
  );
140
149
 
141
150
  const computedStyle: ComputedRef<CSSProperties> = computed(() => ({
@@ -149,6 +158,10 @@ export default defineComponent({
149
158
  function close() {
150
159
  isOpen.value = false;
151
160
  emit('update:open', false);
161
+ onCloseEmit();
162
+ }
163
+
164
+ function onCloseEmit() {
152
165
  emit('onClose');
153
166
  if (props.onClose) {
154
167
  props.onClose();
@@ -162,6 +175,11 @@ export default defineComponent({
162
175
 
163
176
  function onDragStart() {
164
177
  zIndex.value = nextZIndex();
178
+ isDragging.value = true;
179
+ }
180
+
181
+ function onDragStop() {
182
+ isDragging.value = false;
165
183
  }
166
184
 
167
185
  function centerDialog() {
@@ -172,6 +190,27 @@ export default defineComponent({
172
190
  }
173
191
  }
174
192
 
193
+ function keepWithInBounds() {
194
+ if (dialog.value) {
195
+ const boundingRect = dialog.value.getBoundingClientRect();
196
+ const maxX = window.innerWidth - boundingRect.width;
197
+ const maxY = window.innerHeight - boundingRect.height;
198
+
199
+ x.value = Math.min(Math.max(0, x.value), maxX);
200
+ y.value = Math.min(Math.max(0, y.value), maxY);
201
+ }
202
+ }
203
+
204
+ useEventListener(window, 'resize', () => {
205
+ if (isOpen.value) {
206
+ if (!props.draggable) {
207
+ nextTick(() => centerDialog())
208
+ } else {
209
+ nextTick(() => keepWithInBounds())
210
+ }
211
+ }
212
+ })
213
+
175
214
  watch(
176
215
  () => props.open,
177
216
  async (newVal) => {
@@ -179,12 +218,12 @@ export default defineComponent({
179
218
  if (newVal) {
180
219
  await nextTick();
181
220
  centerDialog();
221
+ emit('onOpen');
182
222
  }
183
223
  },
184
- {immediate: true},
224
+ { immediate: true }
185
225
  );
186
226
 
187
- // Expose methods to parent components
188
227
  expose({
189
228
  close,
190
229
  });
@@ -198,6 +237,7 @@ export default defineComponent({
198
237
  modalScale,
199
238
  style,
200
239
  computedStyle,
240
+ headerClasses,
201
241
  backdropClasses,
202
242
  resolvedTitle,
203
243
  wrapperClasses,
@@ -1,138 +1,147 @@
1
- import {
2
- createVar,
3
- globalKeyframes,
4
- style,
5
- styleVariants
6
- } from '@vanilla-extract/css';
7
- import { themeVars } from '../../../styles/theme.css';
8
-
9
- export const vars = {};
10
-
11
- export const root = style({});
12
- export const backdrop = style({
13
- position: 'fixed',
14
- inset: 0,
15
- top: 0,
16
- bottom: 0,
17
- left: 0,
18
- right: 0,
19
- display: 'flex',
20
- justifyContent: 'center',
21
- alignItems: 'center',
22
- pointerEvents: 'none'
23
- });
24
-
25
- export const backdropColor = style({
26
- backgroundColor: 'rgba(0,0,0,.45)'
27
- });
28
-
29
- export const wrapper = style({
30
- position: 'fixed',
31
- inset: 0,
32
- top: 0,
33
- left: 0,
34
- right: 0,
35
- bottom: 0,
36
- outline: 0,
37
- overflow: 'auto',
38
- boxSizing: 'border-box',
39
- });
40
-
41
- export const notModal = style({
42
- pointerEvents: 'none'
43
- })
44
-
45
- export const dialog = style({
46
- boxSizing: 'border-box',
47
- margin: '0 auto',
48
- padding: 0,
49
- position: 'relative',
50
- top: '100px',
51
- width: 'auto',
52
- maxWidth: `calc(100vw - 32px)`,
53
- paddingBottom: '24px'
54
- });
55
-
56
- export const content = style({
57
- position: 'relative',
58
- backgroundColor: '#FFF',
59
- backgroundClip: 'padding-box',
60
- border: `1px solid ${themeVars.color.gray[300]}`,
61
- borderRadius: themeVars.rounded.medium,
62
- pointerEvents: 'auto',
63
- boxShadow: `2px 2px 8px rgba(128, 128, 128, 0.1), 0 9px 28px 8px rgba(0,0,0,.05)`
64
- });
65
-
66
- export const header = style({
67
- color: themeVars.color.font.default,
68
- backgroundColor: '#FFF',
69
- marginBottom: themeVars.space[2],
70
- borderBottom: `1px solid ${themeVars.color.gray[300]}`,
71
- height: `35px`,
72
- display: `flex`,
73
- alignItems: `center`,
74
- padding: `0px ${themeVars.space[2]}`
75
- });
76
-
77
- export const footer = style({
78
- textAlign: 'end',
79
- background: `0 0`,
80
- marginTop: themeVars.space[2]
81
- });
82
-
83
- export const title = style({
84
- margin: 0,
85
- color: themeVars.color.font.default,
86
- fontWeight: 600,
87
- lineHeight: 1.5,
88
- wordWrap: 'break-word'
89
- });
90
-
91
- export const close = style([
92
- {
93
- outline: `none ${themeVars.color.gray[300]}`,
94
- display: 'inline-block',
95
- position: 'absolute',
96
- top: themeVars.space[1.5],
97
- right: themeVars.space[1.5],
98
- width: '1.5rem',
99
- height: '1.5rem',
100
- backgroundColor: 'transparent',
101
- border: 'none',
102
- padding: 0,
103
- cursor: 'pointer',
104
- transition: 'all 0.2s',
105
- borderRadius: themeVars.rounded.full,
106
- marginLeft: themeVars.space[2],
107
- ':hover': {
108
- backgroundColor: themeVars.color.gray[150]
109
- },
110
- ':focus': {
111
- backgroundColor: themeVars.color.gray[150]
112
- },
113
- '::before': {
114
- content: '""',
115
- position: 'absolute',
116
- top: '50%',
117
- left: '50%',
118
- transform: 'translate(-50%, -50%) rotate(45deg)',
119
- width: '0.8rem',
120
- height: '2px',
121
- backgroundColor: themeVars.color.gray[500],
122
- transformOrigin: 'center',
123
- transition: 'all 0.2s'
124
- },
125
- '::after': {
126
- content: '""',
127
- position: 'absolute',
128
- top: '50%',
129
- left: '50%',
130
- transform: 'translate(-50%, -50%) rotate(-45deg)',
131
- width: '0.8rem',
132
- height: '2px',
133
- backgroundColor: themeVars.color.gray[500],
134
- transformOrigin: 'center',
135
- transition: 'all 0.2s'
136
- }
137
- }
138
- ]);
1
+ import {
2
+ createVar,
3
+ globalKeyframes,
4
+ style,
5
+ styleVariants
6
+ } from '@vanilla-extract/css';
7
+ import { themeVars } from '../../../styles/theme.css';
8
+
9
+ export const vars = {};
10
+
11
+ export const root = style({});
12
+ export const backdrop = style({
13
+ position: 'fixed',
14
+ inset: 0,
15
+ top: 0,
16
+ bottom: 0,
17
+ left: 0,
18
+ right: 0,
19
+ display: 'flex',
20
+ justifyContent: 'center',
21
+ alignItems: 'center',
22
+ pointerEvents: 'none'
23
+ });
24
+
25
+ export const backdropColor = style({
26
+ backgroundColor: 'rgba(0,0,0,.45)'
27
+ });
28
+
29
+ export const wrapper = style({
30
+ position: 'fixed',
31
+ inset: 0,
32
+ top: 0,
33
+ left: 0,
34
+ right: 0,
35
+ bottom: 0,
36
+ outline: 0,
37
+ overflow: 'auto',
38
+ boxSizing: 'border-box',
39
+ });
40
+
41
+ export const notModal = style({
42
+ pointerEvents: 'none'
43
+ })
44
+
45
+ export const dialog = style({
46
+ boxSizing: 'border-box',
47
+ margin: '0 auto',
48
+ padding: 0,
49
+ position: 'relative',
50
+ top: '100px',
51
+ width: 'auto',
52
+ maxWidth: `calc(100vw - 32px)`,
53
+ paddingBottom: '24px'
54
+ });
55
+
56
+ export const content = style({
57
+ position: 'relative',
58
+ backgroundColor: '#FFF',
59
+ backgroundClip: 'padding-box',
60
+ border: `1px solid ${themeVars.color.gray[300]}`,
61
+ borderRadius: themeVars.rounded.medium,
62
+ pointerEvents: 'auto',
63
+ boxShadow: `2px 2px 8px rgba(128, 128, 128, 0.1), 0 9px 28px 8px rgba(0,0,0,.05)`
64
+ });
65
+
66
+ export const header = style({
67
+ color: themeVars.color.font.default,
68
+ backgroundColor: '#FFF',
69
+ marginBottom: themeVars.space[2],
70
+ borderBottom: `1px solid ${themeVars.color.gray[300]}`,
71
+ height: `35px`,
72
+ display: `flex`,
73
+ alignItems: `center`,
74
+ padding: `0px ${themeVars.space[2]}`,
75
+ borderRadius: `${themeVars.rounded.medium} ${themeVars.rounded.medium} 0 0`,
76
+ });
77
+
78
+ export const draggable = style({
79
+ cursor: "grab"
80
+ });
81
+
82
+ export const dragging = style({
83
+ cursor: "grabbing"
84
+ });
85
+
86
+ export const footer = style({
87
+ textAlign: 'end',
88
+ background: `0 0`,
89
+ marginTop: themeVars.space[2]
90
+ });
91
+
92
+ export const title = style({
93
+ margin: 0,
94
+ color: themeVars.color.font.default,
95
+ fontWeight: 600,
96
+ lineHeight: 1.5,
97
+ wordWrap: 'break-word',
98
+ });
99
+
100
+ export const close = style([
101
+ {
102
+ outline: `none ${themeVars.color.gray[300]}`,
103
+ display: 'inline-block',
104
+ position: 'absolute',
105
+ top: themeVars.space[1.5],
106
+ right: themeVars.space[1.5],
107
+ width: '1.5rem',
108
+ height: '1.5rem',
109
+ backgroundColor: 'transparent',
110
+ border: 'none',
111
+ padding: 0,
112
+ cursor: 'pointer',
113
+ transition: 'all 0.2s',
114
+ borderRadius: themeVars.rounded.full,
115
+ marginLeft: themeVars.space[2],
116
+ ':hover': {
117
+ backgroundColor: themeVars.color.gray[150]
118
+ },
119
+ ':focus': {
120
+ backgroundColor: themeVars.color.gray[150]
121
+ },
122
+ '::before': {
123
+ content: '""',
124
+ position: 'absolute',
125
+ top: '50%',
126
+ left: '50%',
127
+ transform: 'translate(-50%, -50%) rotate(45deg)',
128
+ width: '0.8rem',
129
+ height: '2px',
130
+ backgroundColor: themeVars.color.gray[500],
131
+ transformOrigin: 'center',
132
+ transition: 'all 0.2s'
133
+ },
134
+ '::after': {
135
+ content: '""',
136
+ position: 'absolute',
137
+ top: '50%',
138
+ left: '50%',
139
+ transform: 'translate(-50%, -50%) rotate(-45deg)',
140
+ width: '0.8rem',
141
+ height: '2px',
142
+ backgroundColor: themeVars.color.gray[500],
143
+ transformOrigin: 'center',
144
+ transition: 'all 0.2s'
145
+ }
146
+ }
147
+ ]);
@@ -1,17 +1,25 @@
1
1
  <template>
2
2
  <slot/>
3
3
  <template v-if="Object.keys(dialogMap).length > 0">
4
- <Teleport to="body">
4
+ <template v-for="dialog in Object.values(dialogMap)">
5
5
  <MDialog
6
- v-for="dialog in Object.values(dialogMap)"
6
+ v-if="dialog.options.open"
7
7
  :key="dialog.key"
8
- v-bind="dialog.options"
8
+ v-bind="filteredOptions(dialog.options)"
9
9
  ref="setDialogRef(dialog.key)"
10
10
  @close="remove(dialog.key)"
11
+ @on-close="handleClose(dialog.key)"
12
+ @on-open="handleOpen(dialog.key)"
11
13
  >
12
- <MRenderer v-if="dialog.options.render" :h="dialog.options.render" :props="dialog.options.props" />
14
+ <template #default>
15
+ <MRenderer
16
+ v-if="dialog.options.render"
17
+ :h="dialog.options.render"
18
+ :props="dialog.options.props"
19
+ />
20
+ </template>
13
21
  </MDialog>
14
- </Teleport>
22
+ </template>
15
23
  </template>
16
24
  </template>
17
25
  <script lang="ts">
@@ -59,6 +67,7 @@ export default defineComponent({
59
67
  dialogComponent.open();
60
68
  } else {
61
69
  dialog.options.open = true;
70
+ handleOpen(key);
62
71
  }
63
72
  },
64
73
  close: () => {
@@ -66,7 +75,7 @@ export default defineComponent({
66
75
  if (dialogComponent && dialogComponent.close) {
67
76
  dialogComponent.close();
68
77
  } else {
69
- remove(key);
78
+ handleClose(key);
70
79
  }
71
80
  },
72
81
  };
@@ -75,11 +84,31 @@ export default defineComponent({
75
84
  return dialog;
76
85
  }
77
86
 
87
+ function filteredOptions(options: DialogOptions) {
88
+ const { render, props, ...filtered } = options;
89
+ return filtered;
90
+ }
91
+
78
92
  function remove(key: string) {
79
93
  delete dialogMap[key];
80
94
  delete dialogRefs.value[key];
81
95
  }
82
96
 
97
+ function handleOpen(key: string) {
98
+ const dialog = dialogMap[key];
99
+ if (dialog && dialog.options.onOpen) {
100
+ dialog.options.onOpen();
101
+ }
102
+ }
103
+
104
+ function handleClose(key: string) {
105
+ const dialog = dialogMap[key];
106
+ if (dialog && dialog.options.onClose) {
107
+ dialog.options.onClose();
108
+ }
109
+ remove(key);
110
+ }
111
+
83
112
  const api: DialogApiInjection = {
84
113
  create,
85
114
  remove,
@@ -90,7 +119,10 @@ export default defineComponent({
90
119
  return {
91
120
  dialogMap,
92
121
  remove,
122
+ handleClose,
123
+ handleOpen,
93
124
  setDialogRef,
125
+ filteredOptions
94
126
  };
95
127
  },
96
128
  });
@@ -41,7 +41,10 @@ function showDialog2() {
41
41
  props: {message: testTitle},
42
42
  open: true,
43
43
  modal: false,
44
- draggable: true
44
+ draggable: false,
45
+ onOpen: () => {
46
+ console.log('hello world')
47
+ }
45
48
  });
46
49
 
47
50
  setTimeout(() => {
@@ -7,6 +7,7 @@ export interface DialogOptions {
7
7
  render?: (props: any) => VNodeChild;
8
8
  props?: { [key: string]: any };
9
9
  onClose?: () => void;
10
+ onOpen?: () => void;
10
11
  open?: boolean;
11
12
  draggable?: boolean;
12
13
  modal?: boolean;