@visualizevalue/mint-app-base 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 (129) hide show
  1. package/.env.example +26 -0
  2. package/README.md +24 -0
  3. package/app/app.vue +7 -0
  4. package/app/assets/styles/animation.css +50 -0
  5. package/app/assets/styles/base.css +34 -0
  6. package/app/assets/styles/cards.css +20 -0
  7. package/app/assets/styles/custom-media.css +4 -0
  8. package/app/assets/styles/custom-selectors.css +1 -0
  9. package/app/assets/styles/forms.css +183 -0
  10. package/app/assets/styles/index.css +11 -0
  11. package/app/assets/styles/normalize.css +541 -0
  12. package/app/assets/styles/prose.css +166 -0
  13. package/app/assets/styles/scroll.css +13 -0
  14. package/app/assets/styles/text.css +14 -0
  15. package/app/assets/styles/utils.css +24 -0
  16. package/app/assets/styles/variables.css +195 -0
  17. package/app/assets/styles/web3-modals.css +26 -0
  18. package/app/components/Account.client.vue +20 -0
  19. package/app/components/Actions.vue +25 -0
  20. package/app/components/AppHeader.vue +99 -0
  21. package/app/components/Authenticated.client.vue +17 -0
  22. package/app/components/Avatar.vue +61 -0
  23. package/app/components/BlocksTimeAgo.client.vue +20 -0
  24. package/app/components/Breadcrumbs.vue +51 -0
  25. package/app/components/Button.vue +98 -0
  26. package/app/components/CardLink.vue +38 -0
  27. package/app/components/CheckSpinner.vue +39 -0
  28. package/app/components/Collection/Intro.vue +111 -0
  29. package/app/components/Collection/OverviewCard.vue +73 -0
  30. package/app/components/Collection/Withdraw.client.vue +61 -0
  31. package/app/components/CollectionsOverview.client.vue +58 -0
  32. package/app/components/Connect.client.vue +88 -0
  33. package/app/components/CountDown.vue +153 -0
  34. package/app/components/DialogFrame.vue +96 -0
  35. package/app/components/ExpandableText.vue +50 -0
  36. package/app/components/Form/Errors.vue +18 -0
  37. package/app/components/Form/Group.vue +57 -0
  38. package/app/components/Form/Input.vue +48 -0
  39. package/app/components/Form/SelectFile.vue +60 -0
  40. package/app/components/GasPrice.client.vue +9 -0
  41. package/app/components/HeaderSection.vue +18 -0
  42. package/app/components/Icon.vue +37 -0
  43. package/app/components/IconLink.vue +29 -0
  44. package/app/components/Image.client.vue +120 -0
  45. package/app/components/Loading.vue +79 -0
  46. package/app/components/MintGasPrice.client.vue +20 -0
  47. package/app/components/MintGasPricePopover.client.vue +69 -0
  48. package/app/components/MintToken.vue +89 -0
  49. package/app/components/MintTokenBar.vue +79 -0
  50. package/app/components/Modal.vue +36 -0
  51. package/app/components/Navbar.client.vue +86 -0
  52. package/app/components/Page/Frame.vue +77 -0
  53. package/app/components/Page/FrameSM.vue +33 -0
  54. package/app/components/Popover.client.vue +119 -0
  55. package/app/components/Profile/Header.client.vue +96 -0
  56. package/app/components/QueryDialog.vue +38 -0
  57. package/app/components/ToggleDarkMode.client.vue +58 -0
  58. package/app/components/Token/Detail.client.vue +194 -0
  59. package/app/components/Token/MintTimeline.client.vue +110 -0
  60. package/app/components/Token/MintTimelineItem.vue +33 -0
  61. package/app/components/Token/OverviewCard.vue +140 -0
  62. package/app/components/TransactionFlow.vue +225 -0
  63. package/app/components/Visual/ImagePreview.vue +8 -0
  64. package/app/composables/account.ts +21 -0
  65. package/app/composables/app.ts +15 -0
  66. package/app/composables/artistData.ts +22 -0
  67. package/app/composables/chainId.ts +25 -0
  68. package/app/composables/collections.ts +435 -0
  69. package/app/composables/darkMode.ts +1 -0
  70. package/app/composables/gasPrice.ts +46 -0
  71. package/app/composables/head.ts +29 -0
  72. package/app/composables/priceFeed.ts +80 -0
  73. package/app/composables/subdomain.ts +27 -0
  74. package/app/error.vue +31 -0
  75. package/app/layouts/default.vue +42 -0
  76. package/app/middleware/lowercaseId.ts +1 -0
  77. package/app/middleware/lowercaseProfileAddress.ts +1 -0
  78. package/app/middleware/redirectUserScope.ts +13 -0
  79. package/app/pages/[id]/[collection]/[tokenId]/index.vue +66 -0
  80. package/app/pages/[id]/[collection]/[tokenId].vue +25 -0
  81. package/app/pages/[id]/[collection]/index.vue +51 -0
  82. package/app/pages/[id]/[collection]/mint.vue +260 -0
  83. package/app/pages/[id]/[collection].vue +24 -0
  84. package/app/pages/[id]/add.vue +40 -0
  85. package/app/pages/[id]/create.vue +177 -0
  86. package/app/pages/[id]/index.vue +43 -0
  87. package/app/pages/[id].vue +9 -0
  88. package/app/pages/index.vue +47 -0
  89. package/app/pages/profile/[address]/index.vue +51 -0
  90. package/app/pages/profile/[address].vue +9 -0
  91. package/app/pages/profile/index.vue +28 -0
  92. package/app/plugins/1.polyfill.client.ts +12 -0
  93. package/app/plugins/2.wagmi.ts +57 -0
  94. package/app/router.options.ts +25 -0
  95. package/app/utils/abis.ts +77 -0
  96. package/app/utils/arrays.ts +1 -0
  97. package/app/utils/artifact.ts +21 -0
  98. package/app/utils/breakpoints.ts +11 -0
  99. package/app/utils/dates.ts +23 -0
  100. package/app/utils/format.ts +60 -0
  101. package/app/utils/images.ts +27 -0
  102. package/app/utils/ipfs.ts +13 -0
  103. package/app/utils/lowercaseRouteParam.ts +10 -0
  104. package/app/utils/serializer.ts +18 -0
  105. package/app/utils/strings.ts +30 -0
  106. package/app/utils/time.ts +23 -0
  107. package/app/utils/types.ts +62 -0
  108. package/app/utils/urls.ts +43 -0
  109. package/nuxt.config.ts +130 -0
  110. package/package.json +44 -0
  111. package/public/apple-touch-icon-512x512.png +0 -0
  112. package/public/example-contract-icon-original.svg +5 -0
  113. package/public/example-contract-icon.svg +5 -0
  114. package/public/favicon.ico +0 -0
  115. package/public/icon.svg +8 -0
  116. package/public/icons/check.svg +3 -0
  117. package/public/icons/opepen.svg +264 -0
  118. package/public/icons/wallets/coinbase.svg +4 -0
  119. package/public/icons/wallets/metamask.svg +1 -0
  120. package/public/icons/wallets/rainbow.svg +59 -0
  121. package/public/icons/wallets/walletconnect.svg +1 -0
  122. package/public/maskable-icon-512x512.png +0 -0
  123. package/public/pwa-192x192.png +0 -0
  124. package/public/pwa-512x512.png +0 -0
  125. package/public/pwa-64x64.png +0 -0
  126. package/server/middleware/log.ts +3 -0
  127. package/server/middleware/subdomain.ts +12 -0
  128. package/server/tsconfig.json +3 -0
  129. package/tsconfig.json +4 -0
@@ -0,0 +1,50 @@
1
+ <template>
2
+ <span :class="{ expanded }" v-html="visibleText"></span>
3
+ <slot
4
+ name="trigger"
5
+ v-if="wasShortened"
6
+ :expanded="expanded"
7
+ :toggle="toggle"
8
+ >
9
+ <Button v-if="wasShortened" @click="toggle" class="link">
10
+ <Icon :type="expanded ? 'chevron-up' : 'chevron-right'" />
11
+ <span>{{ expanded ? collapseText : expandText }}</span>
12
+ </Button>
13
+ </slot>
14
+ </template>
15
+
16
+ <script setup>
17
+ const { length, text } = defineProps({
18
+ length: {
19
+ type: Number,
20
+ default: 120,
21
+ },
22
+ text: String,
23
+ expandText: {
24
+ type: String,
25
+ default: 'Expand',
26
+ },
27
+ collapseText: {
28
+ type: String,
29
+ default: 'Collapse',
30
+ },
31
+ })
32
+
33
+ const cleaned = computed(() => cleanText(text))
34
+ const shortened = computed(() => shortenedCleanText(extractURLs(cleaned.value).text, length))
35
+ const wasShortened = computed(() => shortened.value !== cleaned.value)
36
+ const expanded = ref(false)
37
+ const toggle = () => expanded.value = !expanded.value
38
+
39
+ const visibleText = computed(() => wasShortened.value && !expanded.value ? shortened.value : cleaned.value)
40
+ </script>
41
+
42
+ <style scoped>
43
+ span.expanded {
44
+ white-space: pre-wrap;
45
+ }
46
+
47
+ .button {
48
+ margin-top: var(--size-3);
49
+ }
50
+ </style>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <div v-if="errors.length" class="errors">
3
+ <p v-for="error in errors">{{ error.message }}</p>
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ defineProps({
9
+ errors: Array,
10
+ })
11
+ </script>
12
+
13
+ <style scoped>
14
+ .errors {
15
+ padding: var(--spacer) 0;
16
+ color: var(--error);
17
+ }
18
+ </style>
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <div class="fieldset-wrapper">
3
+ <fieldset>
4
+ <slot />
5
+ </fieldset>
6
+ </div>
7
+ </template>
8
+
9
+ <style scoped>
10
+ .fieldset-wrapper {
11
+ max-width: -webkit-fill-available;
12
+ container-type: inline-size;
13
+ width: 100%;
14
+ }
15
+
16
+ fieldset {
17
+ width: 100%;
18
+ max-width: -webkit-fill-available;
19
+ display: flex;
20
+ flex-wrap: wrap;
21
+
22
+ > * {
23
+ width: 100cqw;
24
+ flex-shrink: 0;
25
+ }
26
+
27
+ @container (min-width: 30rem) {
28
+ flex-wrap: nowrap;
29
+
30
+ > * {
31
+ width: fit-content;
32
+ flex-shrink: 1;
33
+ }
34
+ }
35
+
36
+ :deep(> .form-item),
37
+ :deep(> .button) {
38
+ + .form-item,
39
+ + .button {
40
+ border-top-color: transparent;
41
+
42
+ &:has(.input:focus) {
43
+ border-top-color: var(--border-color-light);
44
+ }
45
+
46
+ @container (min-width: 30rem) {
47
+ border-top-color: var(--border-color);
48
+ border-left-color: transparent;
49
+
50
+ &:has(.input:focus) {
51
+ border-left-color: var(--border-color-light);
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+ </style>
@@ -0,0 +1,48 @@
1
+ <template>
2
+ <div class="form-item">
3
+ <span v-if="prefix" class="prefix">{{ prefix }}</span>
4
+
5
+ <input
6
+ v-bind="inputAttributes"
7
+ @input="$emit('update:modelValue', $event.target.value)"
8
+ autocorrect="off"
9
+ autocapitalize="off"
10
+ spellcheck="false"
11
+ class="input"
12
+ />
13
+
14
+ <span v-if="suffix" class="suffix">{{ suffix }}</span>
15
+ </div>
16
+ </template>
17
+
18
+ <script setup>
19
+ const props = defineProps({
20
+ modelValue: [String, Number],
21
+ type: {
22
+ type: String,
23
+ default: 'text',
24
+ },
25
+ prefix: String,
26
+ suffix: String,
27
+ placeholder: String,
28
+ disabled: Boolean,
29
+ required: Boolean,
30
+ name: String,
31
+ min: String,
32
+ max: String,
33
+ autocomplete: String,
34
+ })
35
+ const emit = defineEmits(['update:modelValue'])
36
+
37
+ const inputAttributes = computed(() => ({
38
+ type: props.type,
39
+ value: props.modelValue,
40
+ placeholder: props.placeholder || '',
41
+ disabled: props.disabled || false,
42
+ required: props.required || false,
43
+ name: props.name || '',
44
+ autocomplete: props.autocomplete || 'on',
45
+ min: props.min,
46
+ max: props.max,
47
+ }))
48
+ </script>
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <Button v-if="! file" type="button" @click="() => open()">
3
+ <Icon type="image" />
4
+ <span>Choose file</span>
5
+ </Button>
6
+ <FormGroup v-else>
7
+ <Button type="button" disabled>
8
+ <span>{{ shortString(file.name) }}</span>
9
+ <span>({{ formatBytes(file.size) }})</span>
10
+ </Button>
11
+ <Button type="button" class="open" @click="() => open()">
12
+ <Icon type="folder" />
13
+ </Button>
14
+ <Button type="button" class="reset" @click="() => reset()">
15
+ <Icon type="trash" />
16
+ </Button>
17
+ </FormGroup>
18
+ </template>
19
+
20
+ <script setup lang="ts">
21
+ import { useFileDialog } from '@vueuse/core'
22
+
23
+ const emit = defineEmits<{
24
+ change: [file: File|null]
25
+ }>()
26
+
27
+ const { files, open, reset, onChange } = useFileDialog({
28
+ accept: 'image/*',
29
+ multiple: false,
30
+ })
31
+
32
+ const file = computed(() => files.value?.length ? files.value[0] : null)
33
+ onChange(() => emit('change', file.value))
34
+ </script>
35
+
36
+ <style scoped>
37
+ fieldset {
38
+ width: fit-content;
39
+ max-width: -webkit-fill-available;
40
+
41
+ .button {
42
+ white-space: nowrap;
43
+
44
+ &:first-child {
45
+ span:last-child {
46
+ text-align: right;
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ .button[disabled] {
53
+ color: var(--color);
54
+ }
55
+
56
+ .open,
57
+ .reset {
58
+ width: min-content;
59
+ }
60
+ </style>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <slot :price="unitPrice">
3
+ <span>{{ unitPrice.formatted.gwei }} GWEI</span>
4
+ </slot>
5
+ </template>
6
+
7
+ <script setup>
8
+ const unitPrice = await useGasPrice()
9
+ </script>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <header>
3
+ <slot />
4
+ </header>
5
+ </template>
6
+
7
+ <style scoped>
8
+ header {
9
+ display: flex;
10
+ gap: var(--spacer);
11
+ justify-content: space-between;
12
+ align-items: center;
13
+
14
+ :deep(> h1) {
15
+ white-space: nowrap;
16
+ }
17
+ }
18
+ </style>
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <i v-if="ICONS[type]" class="icon">
3
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" v-html="ICONS[type]"></svg>
4
+ </i>
5
+ <FeatherIcon v-else :type="type" class="icon" />
6
+ </template>
7
+
8
+ <script setup>
9
+ import FeatherIcon from 'vue-feather'
10
+
11
+ const props = defineProps({
12
+ type: String,
13
+ })
14
+
15
+ const ICONS = {
16
+ 'x.com': `<path d="M13.5222 10.7749L19.4785 4H18.0671L12.8952 9.88256L8.76437 4H4L10.2466 12.8955L4 20H5.41155L10.8732 13.7878L15.2356 20H20L13.5222 10.7749Z" fill="currentColor"/><path d="M11.5889 12.9738L10.956 12.0881M12.1522 10.728L12.7851 11.6137L18.0677 19.0075M13.5222 10.7749L19.4785 4H18.0671L12.8952 9.88256L8.76437 4H4L10.2466 12.8955L4 20H5.41155L10.8732 13.7878L15.2356 20H20L13.5222 10.7749Z" stroke="currentColor"/>`,
17
+ vv: `<path d="M11 14.0646C11 15.0625 10.6833 15.8796 10.05 16.5162L6.6 20H4.4L0.949997 16.5162C0.316667 15.8796 0 15.0625 0 14.0646V5H2.575V13.9614C2.575 14.3915 2.68333 14.7183 2.9 14.942L5.525 17.6775L8.1 14.942C8.31669 14.7183 8.425 14.3915 8.425 13.9614V5H11V14.0646Z" fill="currentColor"/><path d="M24 14.0646C24 15.0625 23.6833 15.8796 23.05 16.5162L19.6 20H17.4L13.95 16.5162C13.3167 15.8796 13 15.0625 13 14.0646V5H15.575V13.9614C15.575 14.3915 15.6833 14.7183 15.9 14.942L18.525 17.6775L21.1 14.942C21.3167 14.7183 21.425 14.3915 21.425 13.9614V5H24V14.0646Z" fill="currentColor"/>`,
18
+ check: `<path fill-rule="evenodd" clip-rule="evenodd" d="M15.36 3.88585C15.0088 3.30969 14.5152 2.83358 13.9268 2.50331C13.3383 2.17304 12.6748 1.99971 12 2C10.577 2 9.33 2.75494 8.64 3.88685C7.98396 3.72742 7.29792 3.73945 6.64787 3.92179C5.99782 4.10413 5.40562 4.45064 4.92823 4.928C4.45083 5.40536 4.10429 5.99751 3.92194 6.64751C3.73959 7.29751 3.72756 7.98349 3.887 8.63948C3.31061 8.99055 2.83426 9.48402 2.50379 10.0724C2.17331 10.6608 1.99982 11.3244 2 11.9992C2 13.4221 2.755 14.668 3.886 15.359C3.72655 16.0149 3.73859 16.7009 3.92094 17.3509C4.10329 18.0009 4.44983 18.5931 4.92723 19.0704C5.40462 19.5478 5.99682 19.8943 6.64687 20.0766C7.29692 20.259 7.98296 20.271 8.639 20.1116C9.1267 20.913 9.88486 21.5137 10.7766 21.8053C11.6684 22.0968 12.635 22.06 13.502 21.7015C14.2737 21.3815 14.9241 20.8252 15.36 20.1126C16.0161 20.2721 16.7023 20.2601 17.3525 20.0777C18.0026 19.8953 18.5949 19.5487 19.0723 19.0712C19.5498 18.5937 19.8963 18.0014 20.0785 17.3513C20.2608 16.7011 20.2727 16.015 20.113 15.359C20.6894 15.0079 21.1657 14.5144 21.4962 13.926C21.8267 13.3376 22.0002 12.6741 22 11.9992C22.0002 11.3244 21.8267 10.6608 21.4962 10.0724C21.1657 9.48402 20.6894 8.99055 20.113 8.63948C20.2723 7.98354 20.2601 7.29765 20.0776 6.64778C19.8951 5.99791 19.5485 5.40592 19.071 4.92877C18.594 4.45131 18.0022 4.10459 17.3525 3.92194C16.7027 3.7393 16.0169 3.72687 15.361 3.88585H15.36ZM11.402 15.5979L15.964 8.75447C16.53 7.90854 15.213 7.03061 14.648 7.87654L10.622 13.9191L9.251 12.5512C8.534 11.8292 7.415 12.9471 8.135 13.6671L10.305 15.8169C10.3913 15.8749 10.4883 15.9152 10.5903 15.9356C10.6923 15.9559 10.7973 15.9558 10.8993 15.9354C11.0013 15.9149 11.0982 15.8745 11.1845 15.8164C11.2708 15.7583 11.3447 15.6837 11.402 15.5969V15.5979Z" fill="currentColor"/>`,
19
+ }
20
+ </script>
21
+
22
+ <style scoped>
23
+ .icon {
24
+ display: inline-block;
25
+ width: var(--size-4);
26
+ height: var(--size-4);
27
+
28
+ :deep(svg) {
29
+ width: 100%;
30
+ height: 100%;
31
+ }
32
+
33
+ &.spin {
34
+ animation: spin 5s infinite linear;
35
+ }
36
+ }
37
+ </style>
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <NuxtLink :to="to" :title="title">
3
+ <Icon :type="type" />
4
+ </NuxtLink>
5
+ </template>
6
+
7
+ <script setup>
8
+ defineProps({
9
+ to: String,
10
+ title: String,
11
+ type: String,
12
+ })
13
+ </script>
14
+
15
+ <style scoped>
16
+ a {
17
+ color: var(--muted);
18
+ transition: all var(--speed);
19
+ width: var(--size-5);
20
+ height: var(--size-5);
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+
25
+ &:--highlight {
26
+ color: var(--color);
27
+ }
28
+ }
29
+ </style>
@@ -0,0 +1,120 @@
1
+ <template>
2
+ <article
3
+ class="image"
4
+ :class="{ loaded }"
5
+ @click="$emit('click')"
6
+ :style="{ padding: `0 0 ${height}` }"
7
+ v-intersection-observer="loadImage"
8
+ >
9
+ <Loading v-if="! loaded" txt=""/>
10
+ <img
11
+ v-if="uri"
12
+ ref="imageEl"
13
+ :alt="alt"
14
+ :src="uri"
15
+ @load="imageLoaded"
16
+ >
17
+ </article>
18
+ </template>
19
+
20
+ <script setup>
21
+ import { vIntersectionObserver } from '@vueuse/components'
22
+
23
+ const props = defineProps({
24
+ src: String,
25
+ alt: String,
26
+ aspectRatio: Number,
27
+ })
28
+ const emit = defineEmits(['click', 'loaded'])
29
+
30
+ const uri = ref('')
31
+ const loaded = ref(false)
32
+ const imageEl = ref(null)
33
+ const aspectRatio = ref(1)
34
+ const computeAspectRatio = () => {
35
+ aspectRatio.value = (
36
+ props.aspectRatio || // The passed aspect ratio
37
+ (imageEl.value?.naturalWidth / (imageEl.value?.naturalHeight || 1)) || // The natural image element ratio
38
+ 1 // The default square ratio
39
+ )
40
+ }
41
+ computeAspectRatio()
42
+ const height = computed(() => (1 / aspectRatio.value) * 100 + '%')
43
+
44
+ const loadImage = ([{ isIntersecting }]) => {
45
+ if (! isIntersecting) return
46
+
47
+ if (! props.src) return
48
+
49
+ uri.value = props.src
50
+ }
51
+ watch(() => props.src, () => loadImage([{ isIntersecting: true }]))
52
+
53
+ // Image loaded event
54
+ const imageLoaded = () => {
55
+ loaded.value = true
56
+ emit('loaded')
57
+ computeAspectRatio()
58
+ }
59
+ </script>
60
+
61
+ <style scoped>
62
+ article.image {
63
+ overflow: hidden;
64
+ background-color: var(--gray-z-2);
65
+ overflow: hidden;
66
+ position: relative;
67
+ height: 0;
68
+ padding-bottom: 100%;
69
+ display: flex;
70
+
71
+ .bordered {
72
+ border-radius: var(--border-radius);
73
+ border: 1px solid var(--border-color);
74
+ }
75
+
76
+ .loader {
77
+ position: absolute;
78
+ top: 0;
79
+ left: 0;
80
+ right: 0;
81
+ bottom: 0;
82
+ }
83
+
84
+ img {
85
+ position: absolute;
86
+ top: 0;
87
+ left: 0;
88
+ right: 0;
89
+ bottom: 0;
90
+ width: 100%;
91
+ height: 100%;
92
+ object-fit: cover;
93
+ object-fit: contain;
94
+ transform: scale(1.2);
95
+ width: 100%;
96
+ opacity: 0;
97
+ transition: all var(--speed);
98
+ }
99
+
100
+ &:not(.loaded) {
101
+ animation: imageLoading alternate infinite 1s;
102
+ }
103
+
104
+ &.loaded {
105
+ img {
106
+ transform: scale(1);
107
+ opacity: 1;
108
+ }
109
+ }
110
+ }
111
+
112
+ @keyframes imageLoading {
113
+ from {
114
+ background-color: var(--gray-z-1);
115
+ }
116
+ to {
117
+ background-color: var(--gray-z-2);
118
+ }
119
+ }
120
+ </style>
@@ -0,0 +1,79 @@
1
+ <template>
2
+ <div class="loader">
3
+ <span class="spinner"></span>
4
+ <span v-if="txt" class="text">{{ txt }}</span>
5
+ </div>
6
+ </template>
7
+
8
+ <script setup>
9
+ const { txt } = defineProps({
10
+ txt: {
11
+ type: String,
12
+ default: 'Loading...',
13
+ },
14
+ })
15
+ </script>
16
+
17
+ <style scoped>
18
+ .loader {
19
+ position: relative;
20
+ z-index: 2;
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ gap: var(--spacer-sm);
25
+
26
+ .text {
27
+ text-transform: var(--text-transform-ui);
28
+ color: var(--muted);
29
+ font-family: var(--font-family-ui);
30
+ font-weight: var(--font-weight-bold);
31
+ }
32
+
33
+ &:not(.inline) {
34
+ text-align: center;
35
+ margin: calc(var(--size-6)) 0;
36
+ }
37
+
38
+ &.inline {
39
+ display: inline-flex;
40
+ gap: var(--size-1);
41
+ margin-left: var(--size-1);
42
+ }
43
+
44
+ &.large {
45
+ font-size: var(--font-size-xl);
46
+
47
+ .spinner {
48
+ border-width: 4px;
49
+ }
50
+ }
51
+
52
+ &.simple {
53
+ .text {
54
+ display: none;
55
+ }
56
+ }
57
+
58
+ .spinner {
59
+ position: relative;
60
+ display: inline-block;
61
+ width: 0.75em;
62
+ height: 0.75em;
63
+ border-radius: 50%;
64
+ border: 2px solid var(--gray-z-6);
65
+ border-right-color: var(--gray-z-3);
66
+ flex-shrink: 0;
67
+ animation: spin infinite linear 1s;
68
+ }
69
+ }
70
+
71
+ @keyframes spin {
72
+ 0% {
73
+ transform: rotate(0);
74
+ }
75
+ 100% {
76
+ transform: rotate(360deg);
77
+ }
78
+ }
79
+ </style>
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <span>
3
+ {{ displayPrice.value }} {{ displayPrice.format }}
4
+
5
+ <span class="usd">(${{ dollarPrice }})</span>
6
+ </span>
7
+ </template>
8
+
9
+ <script setup>
10
+ const props = defineProps({
11
+ mintCount: {
12
+ type: Number,
13
+ default: 1,
14
+ }
15
+ })
16
+ const mintCount = computed(() => props.mintCount)
17
+ const { price, displayPrice } = await useMintPrice(mintCount)
18
+ const priceFeed = usePriceFeedStore()
19
+ const dollarPrice = computed(() => priceFeed.weiToUSD(price.value))
20
+ </script>
@@ -0,0 +1,69 @@
1
+ <template>
2
+ <Popover id="about-gas">
3
+ <template #trigger><MintGasPrice :mint-count="2" /></template>
4
+ <template #content>
5
+ <div class="minimal-prose">
6
+ <h1>Mint Pricing</h1>
7
+ <p>Artifacts are priced based on the ethereum network fees at the time of collecting.</p>
8
+ <p>Network fees (Gas fees) are an essential component of of securing and running decentralized blockchains.</p>
9
+ <p>
10
+ The cost to store and secure the object on the network is mirrored as compensation to the artist,
11
+ creating a direct link between network value and creator reward.
12
+ </p>
13
+ <table>
14
+ <tbody>
15
+ <tr>
16
+ <td>Gas price</td>
17
+ <td><GasPrice /></td>
18
+ </tr>
19
+ <tr>
20
+ <td>Cost to mint</td>
21
+ <td>
22
+ <span>
23
+ 60.000 gas * <GasPrice />
24
+ </span>
25
+ </td>
26
+ </tr>
27
+ <tr>
28
+ <td>Ethereum fee (50%)</td>
29
+ <td><MintGasPrice /></td>
30
+ </tr>
31
+ <tr>
32
+ <td>Artist <abbr title="Artist Compensation">comp.</abbr> (50%)</td>
33
+ <td><MintGasPrice /></td>
34
+ </tr>
35
+ <tr>
36
+ <td>Mint price</td>
37
+ <td><MintGasPrice :mint-count="2" /></td>
38
+ </tr>
39
+ </tbody>
40
+ </table>
41
+ </div>
42
+ </template>
43
+ </Popover>
44
+ </template>
45
+
46
+ <script setup>
47
+ </script>
48
+
49
+ <style scoped>
50
+ table {
51
+ width: 100%;
52
+
53
+ td {
54
+ > div {
55
+ display: grid;
56
+
57
+ > span {
58
+ white-space: nowrap;
59
+ }
60
+ }
61
+ }
62
+
63
+ :deep(.usd) {
64
+ display: inline-block;
65
+ margin-left: auto;
66
+ }
67
+ }
68
+
69
+ </style>