88 :class =" { 'no-image': !featuredImage }"
99 tabindex =" -1"
1010 :to =" `/${projectTypeUrl}/${id}`"
11- :style =" color ? `background-color: ${toColor };` : ''"
11+ :style =" color ? `background-color: ${rgbColor };` : ''"
1212 >
1313 <img v-if =" featuredImage" :src =" featuredImage" alt =" gallery image" loading =" lazy" />
1414 </router-link >
5757 </div >
5858 <div v-if =" showUpdatedDate" v-tooltip =" updatedDate" class =" stat date" >
5959 <EditIcon aria-hidden =" true" />
60- <span class =" date-label" >Updated </span > {{ sinceUpdated }}
60+ <span class =" date-label" >Updated </span > {{ sinceUpdate }}
6161 </div >
6262 <div v-else v-tooltip =" createdDate" class =" stat date" >
6363 <CalendarIcon aria-hidden =" true" />
6767 </article >
6868</template >
6969
70- <script setup>
70+ <script setup lang="ts">
71+ /* eslint vue/require-default-prop: "off" -- https://github.com/vuejs/eslint-plugin-vue/issues/2051 */
72+
73+ import { computed } from ' vue'
7174import { formatNumber } from ' @/helpers'
7275import {
7376 Badge ,
@@ -83,147 +86,69 @@ import {
8386import dayjs from ' dayjs'
8487import relativeTime from ' dayjs/plugin/relativeTime.js'
8588dayjs .extend (relativeTime )
86- </script >
8789
88- <script >
89- import { defineComponent } from ' vue'
90- export default defineComponent ({
91- props: {
92- id: {
93- type: String ,
94- default: ' modrinth-0' ,
95- },
96- type: {
97- type: String ,
98- default: ' mod' ,
99- },
100- name: {
101- type: String ,
102- default: ' Project Name' ,
103- },
104- author: {
105- type: String ,
106- default: null ,
107- },
108- description: {
109- type: String ,
110- default: ' A _type description' ,
111- },
112- iconUrl: {
113- type: String ,
114- default: ' #' ,
115- required: false ,
116- },
117- downloads: {
118- type: String ,
119- default: null ,
120- required: false ,
121- },
122- follows: {
123- type: String ,
124- default: null ,
125- required: false ,
126- },
127- createdAt: {
128- type: String ,
129- default: ' 0000-00-00' ,
130- },
131- updatedAt: {
132- type: String ,
133- default: null ,
134- },
135- categories: {
136- type: Array ,
137- default () {
138- return []
139- },
140- },
141- filteredCategories: {
142- type: Array ,
143- default () {
144- return []
145- },
146- },
147- projectTypeDisplay: {
148- type: String ,
149- default: null ,
150- },
151- projectTypeUrl: {
152- type: String ,
153- default: null ,
154- },
155- status: {
156- type: String ,
157- default: null ,
90+ /** Creates a type that contains original T string, but allows other string values as well. */
91+ type LaxString <T extends string > = T | (string & Record <never , never >)
92+
93+ type ProjectType = ' mod' | ' plugin' | ' modpack' | ' resourcepack'
94+ type SideRequirement = ' required' | ' optional' | ' unsupported'
95+
96+ const props = withDefaults (
97+ defineProps <{
98+ id: string
99+ type: LaxString <ProjectType >
100+ name: string
101+ author? : string
102+ description: string
103+ iconUrl? : string
104+ downloads? : string
105+ follows? : string
106+ createdAt? : string
107+ updatedAt? : string
108+ categories? : string []
109+ projectTypeDisplay: string
110+ projectTypeUrl: string
111+ status? : string
112+ serverSide? : LaxString <SideRequirement >
113+ clientSide? : LaxString <SideRequirement >
114+ moderation? : boolean
115+ search? : boolean
116+ featuredImage? : string
117+ showUpdatedDate? : boolean
118+ hideLoaders? : boolean
119+ color? : number
120+ }>(),
121+ {
122+ id: ' modrinth-0' ,
123+ type: ' mod' ,
124+ name: ' Project Name' ,
125+ description: ' A _type description' , // ???
126+ iconUrl: ' #' ,
127+ createdAt: ' 0000-00-00' ,
128+ categories() {
129+ return []
158130 },
159- serverSide: {
160- type: String ,
161- required: false ,
162- default: ' ' ,
163- },
164- clientSide: {
165- type: String ,
166- required: false ,
167- default: ' ' ,
168- },
169- moderation: {
170- type: Boolean ,
171- required: false ,
172- default: false ,
173- },
174- search: {
175- type: Boolean ,
176- required: false ,
177- default: false ,
178- },
179- featuredImage: {
180- type: String ,
181- required: false ,
182- default: null ,
183- },
184- showUpdatedDate: {
185- type: Boolean ,
186- required: false ,
187- default: true ,
188- },
189- hideLoaders: {
190- type: Boolean ,
191- required: false ,
192- default: false ,
193- },
194- color: {
195- type: Number ,
196- required: false ,
197- default: null ,
198- },
199- },
200- computed: {
201- toColor () {
202- let color = this .color
203-
204- color >>>= 0
205- const b = color & 0xff
206- const g = (color & 0xff00 ) >>> 8
207- const r = (color & 0xff0000 ) >>> 16
208- return ' rgba(' + [r, g, b, 1 ].join (' ,' ) + ' )'
209- },
210- createdDate () {
211- return dayjs (this .createdAt ).format (' MMMM D, YYYY [at] h:mm:ss A' )
212- },
213- sinceCreation () {
214- return dayjs (this .createdAt ).fromNow ()
215- },
216- updatedDate () {
217- return dayjs (this .updatedAt ).format (' MMMM D, YYYY [at] h:mm:ss A' )
218- },
219- sinceUpdated () {
220- return dayjs (this .updatedAt ).fromNow ()
221- },
222- },
223- methods: {
224- formatNumber,
225- },
131+ moderation: false ,
132+ search: false ,
133+ showUpdatedDate: false ,
134+ hideLoaders: false ,
135+ }
136+ )
137+
138+ const rgbColor = computed (() => {
139+ const color = (props .color ?? 0 ) >>> 0
140+ const b = color & 0xff
141+ const g = (color & 0xff00 ) >>> 8
142+ const r = (color & 0xff0000 ) >>> 16
143+ return ` rgb(${r }, ${g }, ${b }) `
226144})
145+
146+ const dateFormat = ' MMMM D, YYYY [at] h:mm:ss A'
147+
148+ const createdDate = computed (() => dayjs (props .createdAt ).format (dateFormat ))
149+ const updatedDate = computed (() => dayjs (props .updatedAt ).format (dateFormat ))
150+ const sinceCreation = computed (() => dayjs (props .createdAt ).fromNow ())
151+ const sinceUpdate = computed (() => dayjs (props .updatedAt ).fromNow ())
227152 </script >
228153
229154<style lang="scss" scoped>
0 commit comments