@1001-digital/components 0.0.4 → 0.0.5

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 (32) hide show
  1. package/package.json +11 -2
  2. package/src/base/components/Button.vue +2 -2
  3. package/src/base/components/Calendar.vue +227 -0
  4. package/src/base/components/Combobox.vue +241 -0
  5. package/src/base/components/ConfirmDialog.vue +33 -0
  6. package/src/base/components/Dialog.vue +7 -1
  7. package/src/base/components/FormDateField.vue +111 -0
  8. package/src/base/components/FormDatePicker.vue +382 -0
  9. package/src/base/components/FormSlider.vue +142 -0
  10. package/src/base/components/FormSwitch.vue +103 -0
  11. package/src/base/components/Globals.vue +9 -0
  12. package/src/base/components/Opepicon.vue +45 -0
  13. package/src/base/components/PinInput.vue +105 -0
  14. package/src/base/components/Progress.vue +66 -0
  15. package/src/base/components/Toasts.vue +6 -1
  16. package/src/base/composables/confirm.ts +29 -0
  17. package/src/base/composables/toast.ts +1 -0
  18. package/src/base/icons.ts +3 -1
  19. package/src/evm/components/EvmAvatar.vue +62 -0
  20. package/src/evm/components/EvmConnect.vue +83 -32
  21. package/src/evm/components/EvmConnectorQR.vue +12 -42
  22. package/src/evm/components/EvmProfile.vue +183 -0
  23. package/src/evm/components/EvmSwitchNetwork.vue +130 -0
  24. package/src/evm/components/EvmTransactionFlow.vue +41 -11
  25. package/src/evm/components/EvmWalletConnectWallets.vue +199 -0
  26. package/src/evm/composables/chainId.ts +2 -2
  27. package/src/evm/composables/uri.ts +11 -0
  28. package/src/evm/composables/walletExplorer.ts +130 -0
  29. package/src/evm/config.ts +1 -0
  30. package/src/evm/index.ts +9 -0
  31. package/src/evm/utils/uri.ts +24 -0
  32. package/src/index.ts +18 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1001-digital/components",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "*.css"
@@ -13,12 +13,16 @@
13
13
  "src"
14
14
  ],
15
15
  "peerDependencies": {
16
+ "@wagmi/core": ">=3.0.0",
16
17
  "@wagmi/vue": ">=0.5.0",
17
18
  "viem": ">=2.0.0",
18
19
  "vue": "^3.5.0",
19
- "@1001-digital/styles": "^0.0.2"
20
+ "@1001-digital/styles": "^0.0.3"
20
21
  },
21
22
  "peerDependenciesMeta": {
23
+ "@wagmi/core": {
24
+ "optional": true
25
+ },
22
26
  "@wagmi/vue": {
23
27
  "optional": true
24
28
  },
@@ -28,6 +32,8 @@
28
32
  },
29
33
  "dependencies": {
30
34
  "@iconify/vue": "^5.0.0",
35
+ "@internationalized/date": "^3.8.0",
36
+ "@visualizevalue/opepicons": "^0.1.0",
31
37
  "@vueuse/core": "^14.2.0",
32
38
  "luxon": "^3.7.0",
33
39
  "qrcode": "^1.5.4",
@@ -36,6 +42,9 @@
36
42
  "devDependencies": {
37
43
  "@types/luxon": "^3.7.0",
38
44
  "@types/qrcode": "^1.5.6",
45
+ "@wagmi/core": "^3.4.0",
46
+ "@wagmi/vue": "^0.5.0",
47
+ "viem": "^2.45.0",
39
48
  "vue": "^3.5.0"
40
49
  }
41
50
  }
@@ -78,7 +78,7 @@ const linkProps = computed(() => {
78
78
  transition: color var(--speed);
79
79
  }
80
80
 
81
- &:has(> .icon:first-child) {
81
+ &:has(> :is(.icon, img):first-child) {
82
82
  padding-inline-start: calc(var(--ui-padding-inline) - var(--size-1));
83
83
 
84
84
  &.small {
@@ -86,7 +86,7 @@ const linkProps = computed(() => {
86
86
  }
87
87
  }
88
88
 
89
- &:has(> .icon:first-child:last-child) {
89
+ &:has(> :is(.icon, img):first-child:last-child) {
90
90
  padding: var(--ui-padding-block);
91
91
  aspect-ratio: 1;
92
92
 
@@ -0,0 +1,227 @@
1
+ <template>
2
+ <CalendarRoot
3
+ v-model="model"
4
+ v-model:placeholder="placeholderValue"
5
+ v-slot="{ grid, weekDays }"
6
+ :disabled="disabled"
7
+ :readonly="readonly"
8
+ :multiple="multiple"
9
+ :locale="locale"
10
+ :week-starts-on="weekStartsOn"
11
+ :min-value="minValue"
12
+ :max-value="maxValue"
13
+ :is-date-disabled="isDateDisabled"
14
+ :is-date-unavailable="isDateUnavailable"
15
+ :number-of-months="numberOfMonths"
16
+ :fixed-weeks="fixedWeeks"
17
+ class="calendar"
18
+ >
19
+ <CalendarHeader class="calendar-header">
20
+ <CalendarPrev class="calendar-nav">
21
+ <Icon type="chevron-left" />
22
+ </CalendarPrev>
23
+ <CalendarHeading class="calendar-heading" />
24
+ <CalendarNext class="calendar-nav">
25
+ <Icon type="chevron-right" />
26
+ </CalendarNext>
27
+ </CalendarHeader>
28
+
29
+ <div class="calendar-grids">
30
+ <CalendarGrid
31
+ v-for="month in grid"
32
+ :key="month.value.toString()"
33
+ :value="month"
34
+ class="calendar-grid"
35
+ >
36
+ <CalendarGridHead>
37
+ <CalendarGridRow class="calendar-grid-row">
38
+ <CalendarHeadCell
39
+ v-for="day in weekDays"
40
+ :key="day"
41
+ class="calendar-head-cell"
42
+ >
43
+ {{ day }}
44
+ </CalendarHeadCell>
45
+ </CalendarGridRow>
46
+ </CalendarGridHead>
47
+
48
+ <CalendarGridBody>
49
+ <CalendarGridRow
50
+ v-for="(row, i) in month.rows"
51
+ :key="i"
52
+ class="calendar-grid-row"
53
+ >
54
+ <CalendarCell
55
+ v-for="date in row"
56
+ :key="date.toString()"
57
+ :date="date"
58
+ class="calendar-cell"
59
+ >
60
+ <CalendarCellTrigger
61
+ :day="date"
62
+ :month="month.value"
63
+ class="calendar-cell-trigger"
64
+ />
65
+ </CalendarCell>
66
+ </CalendarGridRow>
67
+ </CalendarGridBody>
68
+ </CalendarGrid>
69
+ </div>
70
+ </CalendarRoot>
71
+ </template>
72
+
73
+ <script setup lang="ts">
74
+ import { ref } from 'vue'
75
+ import type { DateValue } from '@internationalized/date'
76
+ import {
77
+ CalendarCell,
78
+ CalendarCellTrigger,
79
+ CalendarGrid,
80
+ CalendarGridBody,
81
+ CalendarGridHead,
82
+ CalendarGridRow,
83
+ CalendarHeadCell,
84
+ CalendarHeader,
85
+ CalendarHeading,
86
+ CalendarNext,
87
+ CalendarPrev,
88
+ CalendarRoot,
89
+ } from 'reka-ui'
90
+ import Icon from './Icon.vue'
91
+
92
+ const props = withDefaults(
93
+ defineProps<{
94
+ disabled?: boolean
95
+ readonly?: boolean
96
+ multiple?: boolean
97
+ locale?: string
98
+ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6
99
+ minValue?: DateValue
100
+ maxValue?: DateValue
101
+ isDateDisabled?: (date: DateValue) => boolean
102
+ isDateUnavailable?: (date: DateValue) => boolean
103
+ numberOfMonths?: number
104
+ fixedWeeks?: boolean
105
+ placeholder?: DateValue
106
+ }>(),
107
+ {
108
+ locale: 'en',
109
+ numberOfMonths: 1,
110
+ },
111
+ )
112
+
113
+ const model = defineModel<DateValue | DateValue[] | undefined>()
114
+ const placeholderValue = ref(props.placeholder) as ReturnType<typeof ref<DateValue | undefined>>
115
+ </script>
116
+
117
+ <style scoped>
118
+ @layer components {
119
+ .calendar {
120
+ display: grid;
121
+ gap: var(--spacer-sm);
122
+ }
123
+
124
+ .calendar-header {
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: space-between;
128
+ gap: var(--spacer-sm);
129
+ }
130
+
131
+ .calendar-heading {
132
+ font-family: var(--font-family);
133
+ font-size: var(--ui-font-size);
134
+ text-transform: var(--ui-text-transform);
135
+ font-weight: 500;
136
+ }
137
+
138
+ .calendar-nav {
139
+ all: unset;
140
+ display: inline-flex;
141
+ align-items: center;
142
+ justify-content: center;
143
+ cursor: pointer;
144
+ block-size: var(--calendar-nav-size);
145
+ inline-size: var(--calendar-nav-size);
146
+ border-radius: var(--calendar-cell-border-radius);
147
+
148
+ &:hover {
149
+ background: var(--calendar-hover-background);
150
+ }
151
+
152
+ &:disabled {
153
+ opacity: 0.5;
154
+ cursor: not-allowed;
155
+ }
156
+ }
157
+
158
+ .calendar-grids {
159
+ display: flex;
160
+ gap: var(--spacer);
161
+ }
162
+
163
+ .calendar-grid {
164
+ border-collapse: collapse;
165
+ }
166
+
167
+ .calendar-grid-row {
168
+ display: flex;
169
+ }
170
+
171
+ .calendar-head-cell {
172
+ font-family: var(--font-family);
173
+ font-size: var(--font-xs);
174
+ text-transform: var(--ui-text-transform);
175
+ color: var(--muted);
176
+ inline-size: var(--calendar-cell-size);
177
+ block-size: var(--calendar-cell-size);
178
+ display: flex;
179
+ align-items: center;
180
+ justify-content: center;
181
+ }
182
+
183
+ .calendar-cell {
184
+ padding: 1px;
185
+ }
186
+
187
+ .calendar-cell-trigger {
188
+ all: unset;
189
+ display: flex;
190
+ align-items: center;
191
+ justify-content: center;
192
+ inline-size: var(--calendar-cell-size);
193
+ block-size: var(--calendar-cell-size);
194
+ border-radius: var(--calendar-cell-border-radius);
195
+ font-size: var(--font-sm);
196
+ cursor: pointer;
197
+
198
+ &:hover {
199
+ background: var(--calendar-hover-background);
200
+ }
201
+
202
+ &[data-selected] {
203
+ background: var(--calendar-selected-background);
204
+ color: var(--calendar-selected-color);
205
+ }
206
+
207
+ &[data-today]:not([data-selected]) {
208
+ border: var(--calendar-today-border);
209
+ }
210
+
211
+ &[data-outside-month] {
212
+ opacity: 0.3;
213
+ }
214
+
215
+ &[data-unavailable] {
216
+ opacity: 0.3;
217
+ text-decoration: line-through;
218
+ cursor: not-allowed;
219
+ }
220
+
221
+ &[data-disabled] {
222
+ opacity: 0.3;
223
+ cursor: not-allowed;
224
+ }
225
+ }
226
+ }
227
+ </style>
@@ -0,0 +1,241 @@
1
+ <template>
2
+ <ComboboxRoot
3
+ v-model="model"
4
+ v-model:open="open"
5
+ class="combobox-root"
6
+ :multiple="multiple"
7
+ :disabled="disabled"
8
+ :name="name"
9
+ :reset-search-term-on-blur="resetSearchTermOnBlur"
10
+ :reset-search-term-on-select="resetSearchTermOnSelect"
11
+ >
12
+ <ComboboxAnchor class="combobox-anchor">
13
+ <ComboboxInput
14
+ class="combobox-input"
15
+ :placeholder="placeholder"
16
+ :display-value="resolveDisplayValue"
17
+ :disabled="disabled"
18
+ />
19
+ <ComboboxTrigger class="combobox-trigger">
20
+ <Icon type="chevron-down" />
21
+ </ComboboxTrigger>
22
+ </ComboboxAnchor>
23
+
24
+ <ComboboxPortal>
25
+ <ComboboxContent
26
+ position="popper"
27
+ :side-offset="4"
28
+ class="combobox-content"
29
+ >
30
+ <ComboboxViewport class="combobox-viewport">
31
+ <ComboboxEmpty class="combobox-empty">
32
+ {{ emptyText }}
33
+ </ComboboxEmpty>
34
+ <ComboboxItem
35
+ v-for="option in options"
36
+ :key="option[valueKey]"
37
+ :value="option[valueKey]"
38
+ class="combobox-item"
39
+ >
40
+ {{ option[labelKey] }}
41
+ <ComboboxItemIndicator class="combobox-indicator">
42
+ <Icon type="check" />
43
+ </ComboboxItemIndicator>
44
+ </ComboboxItem>
45
+ </ComboboxViewport>
46
+ </ComboboxContent>
47
+ </ComboboxPortal>
48
+ </ComboboxRoot>
49
+ </template>
50
+
51
+ <script setup lang="ts">
52
+ import {
53
+ ComboboxAnchor,
54
+ ComboboxContent,
55
+ ComboboxEmpty,
56
+ ComboboxInput,
57
+ ComboboxItem,
58
+ ComboboxItemIndicator,
59
+ ComboboxPortal,
60
+ ComboboxRoot,
61
+ ComboboxTrigger,
62
+ ComboboxViewport,
63
+ } from 'reka-ui'
64
+ import Icon from './Icon.vue'
65
+
66
+ const model = defineModel<string | string[]>()
67
+ const open = defineModel<boolean>('open', { default: false })
68
+
69
+ const props = withDefaults(
70
+ defineProps<{
71
+ options?: Record<string, any>[]
72
+ placeholder?: string
73
+ multiple?: boolean
74
+ disabled?: boolean
75
+ valueKey?: string
76
+ labelKey?: string
77
+ name?: string
78
+ emptyText?: string
79
+ resetSearchTermOnBlur?: boolean
80
+ resetSearchTermOnSelect?: boolean
81
+ }>(),
82
+ {
83
+ options: () => [],
84
+ placeholder: 'Search...',
85
+ valueKey: 'value',
86
+ labelKey: 'label',
87
+ emptyText: 'No results found.',
88
+ resetSearchTermOnBlur: true,
89
+ resetSearchTermOnSelect: true,
90
+ },
91
+ )
92
+
93
+ const resolveLabel = (v: any) => {
94
+ const option = props.options.find(o => o[props.valueKey] === v)
95
+ return option ? String(option[props.labelKey]) : String(v)
96
+ }
97
+
98
+ const resolveDisplayValue = (val: any) => {
99
+ if (Array.isArray(val)) return val.map(resolveLabel).join(', ')
100
+ return resolveLabel(val)
101
+ }
102
+ </script>
103
+
104
+ <style>
105
+ @layer components {
106
+ .combobox-root {
107
+ inline-size: 100%;
108
+ }
109
+
110
+ .combobox-anchor {
111
+ display: flex;
112
+ align-items: center;
113
+ gap: var(--size-1);
114
+ background: var(--combobox-background);
115
+ border-radius: var(--combobox-border-radius);
116
+ box-shadow: var(--border-shadow);
117
+ padding-inline-end: var(--size-2);
118
+ transition: box-shadow var(--speed);
119
+
120
+ &:focus-within {
121
+ box-shadow: var(--border-shadow-highlight);
122
+ }
123
+
124
+ &[data-disabled] {
125
+ opacity: 0.5;
126
+ cursor: not-allowed;
127
+ }
128
+ }
129
+
130
+ .combobox-input {
131
+ all: unset;
132
+ flex: 1;
133
+ padding: var(--size-2) var(--size-3);
134
+ font-family: var(--font-family);
135
+ font-size: var(--ui-font-size);
136
+ color: var(--color);
137
+ min-inline-size: 0;
138
+
139
+ &::placeholder {
140
+ color: var(--muted);
141
+ }
142
+
143
+ &:disabled {
144
+ cursor: not-allowed;
145
+ }
146
+ }
147
+
148
+ .combobox-trigger {
149
+ all: unset;
150
+ display: inline-flex;
151
+ align-items: center;
152
+ justify-content: center;
153
+ color: var(--muted);
154
+ cursor: pointer;
155
+ transition: transform var(--speed);
156
+
157
+ [data-state='open'] > & {
158
+ transform: rotate(180deg);
159
+ }
160
+
161
+ &[data-disabled] {
162
+ cursor: not-allowed;
163
+ }
164
+ }
165
+
166
+ .combobox-content {
167
+ background: var(--combobox-background);
168
+ border: var(--combobox-border);
169
+ border-radius: var(--combobox-border-radius);
170
+ box-shadow: var(--shadow-lg);
171
+ z-index: var(--z-index-dropdown);
172
+ min-inline-size: var(--reka-combobox-trigger-width);
173
+ max-block-size: var(--reka-combobox-content-available-height);
174
+ overflow: hidden;
175
+
176
+ /* Entry/exit animations */
177
+ opacity: 1;
178
+ scale: 1;
179
+ transition:
180
+ opacity var(--speed) ease,
181
+ scale var(--speed) ease;
182
+
183
+ @starting-style {
184
+ opacity: 0;
185
+ scale: 0.95;
186
+ }
187
+
188
+ &[data-state='closed'] {
189
+ opacity: 0;
190
+ scale: 0.95;
191
+ }
192
+
193
+ &:focus {
194
+ outline: none;
195
+ }
196
+ }
197
+
198
+ .combobox-viewport {
199
+ padding: var(--combobox-padding);
200
+ }
201
+
202
+ .combobox-empty {
203
+ padding: var(--size-2) var(--size-3);
204
+ color: var(--muted);
205
+ font-family: var(--font-family);
206
+ font-size: var(--ui-font-size);
207
+ user-select: none;
208
+ }
209
+
210
+ .combobox-item {
211
+ padding: var(--size-2) var(--size-3);
212
+ border-radius: calc(var(--combobox-border-radius) / 2);
213
+ display: flex;
214
+ align-items: center;
215
+ justify-content: space-between;
216
+ gap: var(--size-2);
217
+ cursor: pointer;
218
+ outline: none;
219
+ user-select: none;
220
+ font-family: var(--font-family);
221
+ font-size: var(--ui-font-size);
222
+
223
+ &[data-highlighted] {
224
+ background: var(--button-background-highlight);
225
+ }
226
+
227
+ &[data-state='checked'] {
228
+ font-weight: 500;
229
+ }
230
+
231
+ &[data-disabled] {
232
+ opacity: 0.5;
233
+ cursor: not-allowed;
234
+ }
235
+ }
236
+
237
+ .combobox-indicator {
238
+ color: var(--accent);
239
+ }
240
+ }
241
+ </style>
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <Dialog
3
+ v-model:open="open"
4
+ :title="state?.title"
5
+ :closable="false"
6
+ :click-outside="false"
7
+ >
8
+ <p v-if="state?.description">{{ state.description }}</p>
9
+
10
+ <template #footer>
11
+ <Button class="tertiary" @click="resolve(false)">
12
+ {{ state?.cancelText || 'Cancel' }}
13
+ </Button>
14
+ <Button @click="resolve(true)">
15
+ {{ state?.okText || 'OK' }}
16
+ </Button>
17
+ </template>
18
+ </Dialog>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import { computed } from 'vue'
23
+ import Dialog from './Dialog.vue'
24
+ import Button from './Button.vue'
25
+ import { useConfirm } from '../composables/confirm'
26
+
27
+ const { state, resolve } = useConfirm()
28
+
29
+ const open = computed({
30
+ get: () => !!state.value,
31
+ set: () => resolve(false),
32
+ })
33
+ </script>
@@ -45,7 +45,7 @@
45
45
  </template>
46
46
 
47
47
  <script setup lang="ts">
48
- import { ref, computed } from 'vue'
48
+ import { ref, computed, onBeforeUnmount } from 'vue'
49
49
  import Icon from './Icon.vue'
50
50
 
51
51
  const dialog = ref<HTMLDialogElement | null>(null)
@@ -123,6 +123,12 @@ const onClickOutside = () => {
123
123
  open.value = false
124
124
  }
125
125
  }
126
+
127
+ onBeforeUnmount(() => {
128
+ if (!props.compat && dialog.value) {
129
+ dialog.value.close()
130
+ }
131
+ })
126
132
  </script>
127
133
 
128
134
  <style>
@@ -0,0 +1,111 @@
1
+ <template>
2
+ <DateFieldRoot
3
+ v-model="model"
4
+ v-model:placeholder="placeholderValue"
5
+ v-slot="{ segments }"
6
+ :disabled="disabled"
7
+ :readonly="readonly"
8
+ :granularity="granularity"
9
+ :locale="locale"
10
+ :min-value="minValue"
11
+ :max-value="maxValue"
12
+ :is-date-unavailable="isDateUnavailable"
13
+ :hour-cycle="hourCycle"
14
+ :name="name"
15
+ :required="required"
16
+ class="form-date-field"
17
+ >
18
+ <template
19
+ v-for="segment in segments"
20
+ :key="segment.part"
21
+ >
22
+ <DateFieldInput
23
+ v-if="segment.part === 'literal'"
24
+ :part="segment.part"
25
+ class="form-date-field-literal"
26
+ >
27
+ {{ segment.value }}
28
+ </DateFieldInput>
29
+ <DateFieldInput
30
+ v-else
31
+ :part="segment.part"
32
+ class="form-date-field-segment"
33
+ >
34
+ {{ segment.value }}
35
+ </DateFieldInput>
36
+ </template>
37
+ </DateFieldRoot>
38
+ </template>
39
+
40
+ <script setup lang="ts">
41
+ import { ref } from 'vue'
42
+ import type { DateValue } from '@internationalized/date'
43
+ import {
44
+ DateFieldInput,
45
+ DateFieldRoot,
46
+ } from 'reka-ui'
47
+
48
+ const props = withDefaults(
49
+ defineProps<{
50
+ disabled?: boolean
51
+ readonly?: boolean
52
+ granularity?: 'day' | 'hour' | 'minute' | 'second'
53
+ locale?: string
54
+ minValue?: DateValue
55
+ maxValue?: DateValue
56
+ isDateUnavailable?: (date: DateValue) => boolean
57
+ hourCycle?: 12 | 24
58
+ name?: string
59
+ required?: boolean
60
+ placeholder?: DateValue
61
+ }>(),
62
+ {
63
+ locale: 'en',
64
+ granularity: 'day',
65
+ },
66
+ )
67
+
68
+ const model = defineModel<DateValue | undefined>()
69
+ const placeholderValue = ref(props.placeholder) as ReturnType<typeof ref<DateValue | undefined>>
70
+ </script>
71
+
72
+ <style scoped>
73
+ @layer components {
74
+ .form-date-field {
75
+ display: inline-flex;
76
+ align-items: center;
77
+ background: var(--date-field-background);
78
+ block-size: var(--form-item-height);
79
+ padding-inline: var(--ui-padding-inline);
80
+ box-shadow: var(--border-shadow);
81
+ border-radius: var(--border-radius);
82
+ font-size: var(--font-sm);
83
+
84
+ &[data-disabled] {
85
+ opacity: 0.5;
86
+ cursor: not-allowed;
87
+ }
88
+ }
89
+
90
+ .form-date-field-literal {
91
+ color: var(--muted);
92
+ padding: 0 1px;
93
+ }
94
+
95
+ .form-date-field-segment {
96
+ padding: var(--date-field-segment-padding);
97
+ border-radius: var(--date-field-segment-border-radius);
98
+ outline: none;
99
+ color: var(--color);
100
+
101
+ &[data-placeholder] {
102
+ color: var(--muted);
103
+ }
104
+
105
+ &:focus {
106
+ background: var(--date-field-segment-focus-background);
107
+ color: var(--date-field-segment-focus-color);
108
+ }
109
+ }
110
+ }
111
+ </style>