@1001-digital/components 0.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.
Files changed (52) hide show
  1. package/package.json +45 -0
  2. package/src/base/components/Actions.vue +57 -0
  3. package/src/base/components/Alert.vue +90 -0
  4. package/src/base/components/Button.vue +260 -0
  5. package/src/base/components/Card.vue +78 -0
  6. package/src/base/components/CardLink.vue +56 -0
  7. package/src/base/components/Dialog.vue +274 -0
  8. package/src/base/components/Dropdown.vue +167 -0
  9. package/src/base/components/DropdownCheckboxItem.vue +30 -0
  10. package/src/base/components/DropdownGroup.vue +9 -0
  11. package/src/base/components/DropdownItem.vue +23 -0
  12. package/src/base/components/DropdownLabel.vue +9 -0
  13. package/src/base/components/DropdownRadioGroup.vue +15 -0
  14. package/src/base/components/DropdownRadioItem.vue +29 -0
  15. package/src/base/components/DropdownSeparator.vue +7 -0
  16. package/src/base/components/DropdownSub.vue +58 -0
  17. package/src/base/components/Form.vue +27 -0
  18. package/src/base/components/FormCheckbox.vue +92 -0
  19. package/src/base/components/FormGroup.vue +39 -0
  20. package/src/base/components/FormInputGroup.vue +55 -0
  21. package/src/base/components/FormItem.vue +89 -0
  22. package/src/base/components/FormLabel.vue +39 -0
  23. package/src/base/components/FormRadioGroup.vue +118 -0
  24. package/src/base/components/FormSelect.vue +160 -0
  25. package/src/base/components/FormTextarea.vue +38 -0
  26. package/src/base/components/Icon.vue +29 -0
  27. package/src/base/components/Loading.vue +81 -0
  28. package/src/base/components/Popover.vue +182 -0
  29. package/src/base/components/Tag.vue +56 -0
  30. package/src/base/components/Tags.vue +13 -0
  31. package/src/base/components/Toasts.vue +254 -0
  32. package/src/base/components/Tooltip.vue +100 -0
  33. package/src/base/composables/time.ts +82 -0
  34. package/src/base/composables/toast.ts +40 -0
  35. package/src/base/icons.ts +20 -0
  36. package/src/base/link.ts +4 -0
  37. package/src/base/utils/format-number.ts +29 -0
  38. package/src/base/utils/time.ts +20 -0
  39. package/src/evm/components/EvmAccount.vue +28 -0
  40. package/src/evm/components/EvmConnect.vue +254 -0
  41. package/src/evm/components/EvmConnectorQR.vue +116 -0
  42. package/src/evm/components/EvmMetaMaskQR.vue +15 -0
  43. package/src/evm/components/EvmTransactionFlow.vue +327 -0
  44. package/src/evm/components/EvmWalletConnectQR.vue +13 -0
  45. package/src/evm/composables/base.ts +7 -0
  46. package/src/evm/composables/chainId.ts +41 -0
  47. package/src/evm/config.ts +32 -0
  48. package/src/evm/index.ts +25 -0
  49. package/src/evm/utils/addresses.ts +6 -0
  50. package/src/evm/utils/chains.ts +32 -0
  51. package/src/evm/utils/format-eth.ts +15 -0
  52. package/src/index.ts +68 -0
@@ -0,0 +1,274 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <Transition
4
+ :css="false"
5
+ @enter="onEnter"
6
+ @leave="onLeave"
7
+ @after-leave="() => emit('closed')"
8
+ >
9
+ <component
10
+ v-if="open"
11
+ ref="dialog"
12
+ :is="tag"
13
+ :class="classes"
14
+ tabindex="-1"
15
+ @cancel.stop.prevent="closable && (open = false)"
16
+ @click="onDialogClick"
17
+ >
18
+ <h1 v-if="title">{{ title }}</h1>
19
+ <button
20
+ v-if="closable"
21
+ class="close"
22
+ :title="`Close ${title || 'Dialog'}`"
23
+ @pointerdown="open = false"
24
+ @click="open = false"
25
+ >
26
+ <Icon type="close" />
27
+ </button>
28
+
29
+ <section>
30
+ <slot />
31
+ </section>
32
+
33
+ <footer v-if="$slots.footer">
34
+ <slot name="footer" />
35
+ </footer>
36
+ </component>
37
+ </Transition>
38
+
39
+ <div
40
+ v-if="compat && open"
41
+ class="overlay"
42
+ @click="onClickOutside"
43
+ ></div>
44
+ </Teleport>
45
+ </template>
46
+
47
+ <script setup lang="ts">
48
+ import { ref, computed } from 'vue'
49
+ import Icon from './Icon.vue'
50
+
51
+ const dialog = ref<HTMLDialogElement | null>(null)
52
+ const props = withDefaults(
53
+ defineProps<{
54
+ title?: string
55
+ class?: string | string[] | Record<string, boolean>
56
+ clickOutside?: boolean
57
+ closable?: boolean
58
+ compat?: boolean
59
+ large?: boolean
60
+ }>(),
61
+ {
62
+ clickOutside: true,
63
+ closable: true,
64
+ },
65
+ )
66
+ const emit = defineEmits<{
67
+ closed: []
68
+ }>()
69
+ const open = defineModel<boolean>('open', { required: true })
70
+ const tag = computed(() => (props.compat ? 'article' : 'dialog'))
71
+ const classes = computed(() => {
72
+ let obj: Record<string, boolean> = {
73
+ dialog: true,
74
+ compat: !!props.compat,
75
+ large: !!props.large,
76
+ }
77
+
78
+ // Apply passed classes
79
+ if (typeof props.class === 'string') {
80
+ obj[props.class] = true
81
+ } else if (Array.isArray(props.class)) {
82
+ props.class.forEach((c) => {
83
+ obj[c] = true
84
+ })
85
+ } else if (typeof props.class === 'object') {
86
+ obj = { ...obj, ...props.class }
87
+ }
88
+
89
+ if (props.compat) {
90
+ obj.open = true
91
+ }
92
+
93
+ return obj
94
+ })
95
+
96
+ const onEnter = (el: Element, done: () => void) => {
97
+ if (!props.compat) {
98
+ ;(el as HTMLDialogElement).showModal()
99
+ }
100
+ // Focus the dialog itself to prevent the close button from gaining focus
101
+ ;(el as HTMLElement).focus()
102
+ done()
103
+ }
104
+
105
+ const onLeave = (el: Element, done: () => void) => {
106
+ el.addEventListener('transitionend', (e) => {
107
+ if ((e as TransitionEvent).propertyName === 'opacity') done()
108
+ })
109
+ if (props.compat) {
110
+ el.classList.remove('open')
111
+ } else {
112
+ ;(el as HTMLDialogElement).close()
113
+ }
114
+ }
115
+
116
+ const onDialogClick = (e: MouseEvent) => {
117
+ if (props.compat || e.target !== dialog.value) return
118
+ onClickOutside()
119
+ }
120
+
121
+ const onClickOutside = () => {
122
+ if (props.clickOutside) {
123
+ open.value = false
124
+ }
125
+ }
126
+ </script>
127
+
128
+ <style>
129
+ @layer components {
130
+ .dialog {
131
+ max-inline-size: min(
132
+ var(--dialog-width, 32rem),
133
+ calc(100vw - var(--spacer) * 2)
134
+ );
135
+ inline-size: 100%;
136
+ background: var(--background);
137
+ color: var(--color);
138
+ border: var(--border);
139
+ border-radius: var(--border-radius);
140
+ padding: 0;
141
+ max-block-size: 80dvh;
142
+ container-type: inline-size;
143
+ display: grid;
144
+ grid-template-rows: auto 1fr auto;
145
+ overflow: hidden;
146
+
147
+ /* Entry/exit animations */
148
+ opacity: 1;
149
+ scale: 1;
150
+ transition:
151
+ opacity var(--speed) ease,
152
+ scale var(--speed) ease,
153
+ overlay var(--speed) ease allow-discrete,
154
+ display var(--speed) ease allow-discrete;
155
+
156
+ @starting-style {
157
+ opacity: 0;
158
+ scale: 0.95;
159
+ }
160
+
161
+ /* Exit animation */
162
+ &:not([open]):not(:popover-open):not(.open) {
163
+ opacity: 0;
164
+ scale: 0.95;
165
+ }
166
+
167
+ &::backdrop {
168
+ background-color: var(--backdrop-background-color);
169
+ backdrop-filter: var(--blur);
170
+ transition:
171
+ background-color var(--speed) ease,
172
+ backdrop-filter var(--speed) ease,
173
+ overlay var(--speed) ease allow-discrete,
174
+ display var(--speed) ease allow-discrete;
175
+
176
+ @starting-style {
177
+ background-color: transparent;
178
+ }
179
+ }
180
+
181
+ &.compat {
182
+ position: fixed;
183
+ inset-block-start: 50%;
184
+ inset-inline-start: 50%;
185
+ z-index: var(--z-index-dialog);
186
+ transform: translate(-50%, -50%);
187
+
188
+ &.open + .overlay {
189
+ position: fixed;
190
+ inset: 0;
191
+ z-index: var(--z-index-overlay);
192
+ background-color: var(--backdrop-background-color);
193
+ backdrop-filter: var(--blur);
194
+ transition:
195
+ background-color var(--speed) ease,
196
+ backdrop-filter var(--speed) ease;
197
+
198
+ @starting-style {
199
+ background-color: transparent;
200
+ }
201
+ }
202
+ }
203
+
204
+ &:focus {
205
+ outline: none;
206
+ }
207
+
208
+ > h1:first-child,
209
+ > .close {
210
+ display: flex;
211
+ align-items: center;
212
+ block-size: calc(var(--spacer) * 2);
213
+ box-shadow: var(--border-shadow);
214
+ padding-inline-start: var(--spacer);
215
+ font-family: var(--font-family);
216
+ font-size: var(--ui-font-size);
217
+ text-transform: var(--ui-text-transform);
218
+ margin: 0;
219
+ }
220
+
221
+ > h1:first-child {
222
+ padding-right: calc(var(--spacer) * 3);
223
+ }
224
+
225
+ > .close {
226
+ position: absolute;
227
+ top: 0;
228
+ right: 0;
229
+ inline-size: calc(var(--spacer) * 2);
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: center;
233
+ padding: 0;
234
+ border-radius: 0;
235
+ border-start-end-radius: var(--border-radius);
236
+
237
+ &:is(:hover, :active, :focus, .active) {
238
+ outline: none;
239
+ }
240
+ }
241
+
242
+ > section {
243
+ overflow-y: auto;
244
+ overscroll-behavior: contain;
245
+ padding: var(--spacer);
246
+ display: grid;
247
+ gap: var(--spacer);
248
+ }
249
+
250
+ > footer {
251
+ display: flex;
252
+ gap: var(--spacer);
253
+ justify-content: flex-end;
254
+ padding: var(--spacer);
255
+ border-block-start: var(--border);
256
+
257
+ &:empty {
258
+ display: none;
259
+ }
260
+ }
261
+
262
+ &.large {
263
+ --dialog-width: min(90vw, 64rem);
264
+ }
265
+ }
266
+
267
+ html:has(dialog[open]),
268
+ body:has(dialog[open]),
269
+ html:has(.dialog.open),
270
+ body:has(.dialog.open) {
271
+ overflow: hidden;
272
+ }
273
+ }
274
+ </style>
@@ -0,0 +1,167 @@
1
+ <template>
2
+ <DropdownMenuRoot
3
+ v-model:open="open"
4
+ :modal="modal"
5
+ :dir="dir"
6
+ >
7
+ <DropdownMenuTrigger as-child>
8
+ <slot name="trigger" />
9
+ </DropdownMenuTrigger>
10
+
11
+ <DropdownMenuPortal>
12
+ <DropdownMenuContent
13
+ class="dropdown"
14
+ :class="props.class"
15
+ :side="side"
16
+ :align="align"
17
+ :side-offset="sideOffset"
18
+ :align-offset="alignOffset"
19
+ :avoid-collisions="avoidCollisions"
20
+ :collision-padding="collisionPadding"
21
+ :loop="loop"
22
+ >
23
+ <div class="dropdown-items">
24
+ <slot />
25
+ </div>
26
+ <DropdownMenuArrow
27
+ v-if="arrow"
28
+ class="dropdown-arrow"
29
+ />
30
+ </DropdownMenuContent>
31
+ </DropdownMenuPortal>
32
+ </DropdownMenuRoot>
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import {
37
+ DropdownMenuArrow,
38
+ DropdownMenuContent,
39
+ DropdownMenuPortal,
40
+ DropdownMenuRoot,
41
+ DropdownMenuTrigger,
42
+ } from 'reka-ui'
43
+
44
+ const props = withDefaults(
45
+ defineProps<{
46
+ class?: string | string[] | Record<string, boolean>
47
+ side?: 'top' | 'right' | 'bottom' | 'left'
48
+ align?: 'start' | 'center' | 'end'
49
+ sideOffset?: number
50
+ alignOffset?: number
51
+ avoidCollisions?: boolean
52
+ collisionPadding?: number
53
+ arrow?: boolean
54
+ modal?: boolean
55
+ loop?: boolean
56
+ dir?: 'ltr' | 'rtl'
57
+ }>(),
58
+ {
59
+ side: 'bottom',
60
+ align: 'start',
61
+ sideOffset: 4,
62
+ avoidCollisions: true,
63
+ collisionPadding: 8,
64
+ loop: true,
65
+ },
66
+ )
67
+
68
+ const open = defineModel<boolean>('open', { required: true })
69
+ </script>
70
+
71
+ <style>
72
+ @layer components {
73
+ .dropdown {
74
+ background: var(--dropdown-background);
75
+ color: var(--color);
76
+ border: var(--dropdown-border);
77
+ border-radius: var(--dropdown-border-radius);
78
+ padding: 0;
79
+ font-family: var(--font-family);
80
+ font-size: var(--ui-font-size);
81
+ z-index: var(--z-index-ui);
82
+ min-inline-size: var(--reka-dropdown-menu-trigger-width);
83
+ max-block-size: var(--reka-dropdown-menu-content-available-height);
84
+ transform-origin: var(--reka-dropdown-menu-content-transform-origin);
85
+
86
+ /* Entry/exit animations */
87
+ opacity: 1;
88
+ scale: 1;
89
+ transition:
90
+ opacity var(--speed) ease,
91
+ scale var(--speed) ease;
92
+
93
+ @starting-style {
94
+ opacity: 0;
95
+ scale: 0.95;
96
+ }
97
+
98
+ &[data-state='closed'] {
99
+ opacity: 0;
100
+ scale: 0.95;
101
+ }
102
+
103
+ &:focus {
104
+ outline: none;
105
+ }
106
+
107
+ .dropdown-arrow {
108
+ fill: var(--dropdown-arrow-fill);
109
+ stroke: var(--border-color);
110
+ }
111
+ }
112
+
113
+ .dropdown-items {
114
+ padding: var(--dropdown-padding);
115
+ overflow-y: auto;
116
+ overscroll-behavior: contain;
117
+ }
118
+
119
+ .dropdown-item {
120
+ padding: var(--size-2) var(--size-3);
121
+ border-radius: calc(var(--dropdown-border-radius) / 2);
122
+ display: flex;
123
+ align-items: center;
124
+ gap: var(--size-2);
125
+ cursor: pointer;
126
+ outline: none;
127
+ user-select: none;
128
+
129
+ &[data-highlighted] {
130
+ background: var(--button-background-highlight);
131
+ }
132
+
133
+ &[data-disabled] {
134
+ opacity: 0.5;
135
+ cursor: not-allowed;
136
+ }
137
+ }
138
+
139
+ .dropdown-label {
140
+ padding: var(--size-2) var(--size-3);
141
+ color: var(--muted);
142
+ font-weight: 500;
143
+ user-select: none;
144
+ }
145
+
146
+ .dropdown-separator {
147
+ block-size: 1px;
148
+ background: var(--border-color);
149
+ margin-block: var(--size-1);
150
+ }
151
+
152
+ .dropdown-item-indicator {
153
+ display: inline-flex;
154
+ align-items: center;
155
+ justify-content: center;
156
+ }
157
+
158
+ .dropdown-sub-trigger {
159
+ justify-content: space-between;
160
+ }
161
+
162
+ .dropdown-sub-icon {
163
+ color: var(--muted);
164
+ margin-inline-start: auto;
165
+ }
166
+ }
167
+ </style>
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <DropdownMenuCheckboxItem
3
+ class="dropdown-item"
4
+ v-model="model"
5
+ :disabled="disabled"
6
+ :text-value="textValue"
7
+ @select="(e: Event) => emit('select', e)"
8
+ >
9
+ <DropdownMenuItemIndicator class="dropdown-item-indicator">
10
+ <Icon type="check" />
11
+ </DropdownMenuItemIndicator>
12
+ <slot />
13
+ </DropdownMenuCheckboxItem>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { DropdownMenuCheckboxItem, DropdownMenuItemIndicator } from 'reka-ui'
18
+ import Icon from './Icon.vue'
19
+
20
+ defineProps<{
21
+ disabled?: boolean
22
+ textValue?: string
23
+ }>()
24
+
25
+ const model = defineModel<boolean | 'indeterminate'>({ default: false })
26
+
27
+ const emit = defineEmits<{
28
+ select: [event: Event]
29
+ }>()
30
+ </script>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <DropdownMenuGroup>
3
+ <slot />
4
+ </DropdownMenuGroup>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { DropdownMenuGroup } from 'reka-ui'
9
+ </script>
@@ -0,0 +1,23 @@
1
+ <template>
2
+ <DropdownMenuItem
3
+ class="dropdown-item"
4
+ :disabled="disabled"
5
+ :text-value="textValue"
6
+ @select="(e: Event) => emit('select', e)"
7
+ >
8
+ <slot />
9
+ </DropdownMenuItem>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { DropdownMenuItem } from 'reka-ui'
14
+
15
+ defineProps<{
16
+ disabled?: boolean
17
+ textValue?: string
18
+ }>()
19
+
20
+ const emit = defineEmits<{
21
+ select: [event: Event]
22
+ }>()
23
+ </script>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <DropdownMenuLabel class="dropdown-label">
3
+ <slot />
4
+ </DropdownMenuLabel>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { DropdownMenuLabel } from 'reka-ui'
9
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <DropdownMenuRadioGroup
3
+ :model-value="model"
4
+ @update:model-value="(v) => (model = v)"
5
+ >
6
+ <slot />
7
+ </DropdownMenuRadioGroup>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { DropdownMenuRadioGroup } from 'reka-ui'
12
+ import type { AcceptableValue } from 'reka-ui'
13
+
14
+ const model = defineModel<AcceptableValue>()
15
+ </script>
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <DropdownMenuRadioItem
3
+ class="dropdown-item"
4
+ :value="value"
5
+ :disabled="disabled"
6
+ :text-value="textValue"
7
+ @select="(e: Event) => emit('select', e)"
8
+ >
9
+ <DropdownMenuItemIndicator class="dropdown-item-indicator">
10
+ <Icon type="check" />
11
+ </DropdownMenuItemIndicator>
12
+ <slot />
13
+ </DropdownMenuRadioItem>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { DropdownMenuItemIndicator, DropdownMenuRadioItem } from 'reka-ui'
18
+ import Icon from './Icon.vue'
19
+
20
+ defineProps<{
21
+ value: string
22
+ disabled?: boolean
23
+ textValue?: string
24
+ }>()
25
+
26
+ const emit = defineEmits<{
27
+ select: [event: Event]
28
+ }>()
29
+ </script>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <DropdownMenuSeparator class="dropdown-separator" />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { DropdownMenuSeparator } from 'reka-ui'
7
+ </script>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <DropdownMenuSub v-model:open="open">
3
+ <DropdownMenuSubTrigger
4
+ class="dropdown-item dropdown-sub-trigger"
5
+ :disabled="disabled"
6
+ >
7
+ <slot name="trigger" />
8
+ <Icon
9
+ class="dropdown-sub-icon"
10
+ type="chevron-right"
11
+ />
12
+ </DropdownMenuSubTrigger>
13
+
14
+ <DropdownMenuPortal>
15
+ <DropdownMenuSubContent
16
+ class="dropdown"
17
+ :class="props.class"
18
+ :side-offset="sideOffset"
19
+ :align-offset="alignOffset"
20
+ :avoid-collisions="avoidCollisions"
21
+ :collision-padding="collisionPadding"
22
+ :loop="loop"
23
+ >
24
+ <slot />
25
+ </DropdownMenuSubContent>
26
+ </DropdownMenuPortal>
27
+ </DropdownMenuSub>
28
+ </template>
29
+
30
+ <script setup lang="ts">
31
+ import {
32
+ DropdownMenuPortal,
33
+ DropdownMenuSub,
34
+ DropdownMenuSubContent,
35
+ DropdownMenuSubTrigger,
36
+ } from 'reka-ui'
37
+ import Icon from './Icon.vue'
38
+
39
+ const props = withDefaults(
40
+ defineProps<{
41
+ class?: string | string[] | Record<string, boolean>
42
+ disabled?: boolean
43
+ sideOffset?: number
44
+ alignOffset?: number
45
+ avoidCollisions?: boolean
46
+ collisionPadding?: number
47
+ loop?: boolean
48
+ }>(),
49
+ {
50
+ sideOffset: -4,
51
+ avoidCollisions: true,
52
+ collisionPadding: 8,
53
+ loop: true,
54
+ },
55
+ )
56
+
57
+ const open = defineModel<boolean>('open', { default: false })
58
+ </script>
@@ -0,0 +1,27 @@
1
+ <template>
2
+ <form class="form">
3
+ <slot />
4
+ </form>
5
+ </template>
6
+
7
+ <style scoped>
8
+ .form {
9
+ display: grid;
10
+ gap: var(--spacer);
11
+
12
+ & :deep(> header),
13
+ & :deep(> footer) {
14
+ display: grid;
15
+ gap: var(--spacer-sm);
16
+
17
+ h1,
18
+ p {
19
+ text-align: start;
20
+ }
21
+
22
+ h1 {
23
+ font-size: var(--font-xl);
24
+ }
25
+ }
26
+ }
27
+ </style>