Add TailwindCSS (#1252)

* Setup TailwindCSS

* Fully setup configuration

* Refactor some tailwind variables
This commit is contained in:
Evan Song
2024-07-06 20:57:32 -07:00
committed by GitHub
parent 0f2ddb452c
commit abec2e48d4
176 changed files with 7905 additions and 7433 deletions

View File

@@ -163,7 +163,7 @@
id: 'withhold-reply',
color: 'danger',
action: () => {
sendReply('withheld')
sendReply('withheld');
},
hoverFilled: true,
disabled: project.status === 'withheld',
@@ -174,7 +174,7 @@
id: 'withhold',
color: 'danger',
action: () => {
setStatus('withheld')
setStatus('withheld');
},
hoverFilled: true,
disabled: project.status === 'withheld',
@@ -196,22 +196,22 @@
</template>
<script setup>
import { OverflowMenu, MarkdownEditor } from '@modrinth/ui'
import { DropdownIcon } from '@modrinth/assets'
import { useImageUpload } from '~/composables/image-upload.ts'
import CopyCode from '~/components/ui/CopyCode.vue'
import ReplyIcon from '~/assets/images/utils/reply.svg?component'
import SendIcon from '~/assets/images/utils/send.svg?component'
import CloseIcon from '~/assets/images/utils/check-circle.svg?component'
import CrossIcon from '~/assets/images/utils/x.svg?component'
import EyeOffIcon from '~/assets/images/utils/eye-off.svg?component'
import CheckIcon from '~/assets/images/utils/check.svg?component'
import ModerationIcon from '~/assets/images/sidebar/admin.svg?component'
import ThreadMessage from '~/components/ui/thread/ThreadMessage.vue'
import { isStaff } from '~/helpers/users.js'
import { isApproved, isRejected } from '~/helpers/projects.js'
import Modal from '~/components/ui/Modal.vue'
import Checkbox from '~/components/ui/Checkbox.vue'
import { OverflowMenu, MarkdownEditor } from "@modrinth/ui";
import { DropdownIcon } from "@modrinth/assets";
import { useImageUpload } from "~/composables/image-upload.ts";
import CopyCode from "~/components/ui/CopyCode.vue";
import ReplyIcon from "~/assets/images/utils/reply.svg?component";
import SendIcon from "~/assets/images/utils/send.svg?component";
import CloseIcon from "~/assets/images/utils/check-circle.svg?component";
import CrossIcon from "~/assets/images/utils/x.svg?component";
import EyeOffIcon from "~/assets/images/utils/eye-off.svg?component";
import CheckIcon from "~/assets/images/utils/check.svg?component";
import ModerationIcon from "~/assets/images/sidebar/admin.svg?component";
import ThreadMessage from "~/components/ui/thread/ThreadMessage.vue";
import { isStaff } from "~/helpers/users.js";
import { isApproved, isRejected } from "~/helpers/projects.js";
import Modal from "~/components/ui/Modal.vue";
import Checkbox from "~/components/ui/Checkbox.vue";
const props = defineProps({
thread: {
@@ -236,166 +236,166 @@ const props = defineProps({
currentMember: {
type: Object,
default() {
return null
return null;
},
},
auth: {
type: Object,
required: true,
},
})
});
const emit = defineEmits(['update-thread'])
const emit = defineEmits(["update-thread"]);
const app = useNuxtApp()
const flags = useFeatureFlags()
const app = useNuxtApp();
const flags = useFeatureFlags();
const members = computed(() => {
const members = {}
const members = {};
for (const member of props.thread.members) {
members[member.id] = member
members[member.id] = member;
}
return members
})
return members;
});
const replyBody = ref('')
const replyBody = ref("");
const sortedMessages = computed(() => {
if (props.thread !== null) {
return props.thread.messages
.slice()
.sort((a, b) => app.$dayjs(a.created) - app.$dayjs(b.created))
.sort((a, b) => app.$dayjs(a.created) - app.$dayjs(b.created));
}
return []
})
return [];
});
const modalSubmit = ref(null)
const modalSubmit = ref(null);
async function updateThreadLocal() {
let threadId = null
let threadId = null;
if (props.project) {
threadId = props.project.thread_id
threadId = props.project.thread_id;
} else if (props.report) {
threadId = props.report.thread_id
threadId = props.report.thread_id;
}
let thread = null
let thread = null;
if (threadId) {
thread = await useBaseFetch(`thread/${threadId}`)
thread = await useBaseFetch(`thread/${threadId}`);
}
emit('update-thread', thread)
emit("update-thread", thread);
}
const imageIDs = ref([])
const imageIDs = ref([]);
async function onUploadImage(file) {
const response = await useImageUpload(file, { context: 'thread_message' })
const response = await useImageUpload(file, { context: "thread_message" });
imageIDs.value.push(response.id)
imageIDs.value.push(response.id);
// Keep the last 10 entries of image IDs
imageIDs.value = imageIDs.value.slice(-10)
imageIDs.value = imageIDs.value.slice(-10);
return response.url
return response.url;
}
async function sendReply(status = null, privateMessage = false) {
try {
const body = {
body: {
type: 'text',
type: "text",
body: replyBody.value,
private: privateMessage,
},
}
};
if (imageIDs.value.length > 0) {
body.body = {
...body.body,
uploaded_images: imageIDs.value,
}
};
}
await useBaseFetch(`thread/${props.thread.id}`, {
method: 'POST',
method: "POST",
body,
})
});
replyBody.value = ''
replyBody.value = "";
await updateThreadLocal()
await updateThreadLocal();
if (status !== null) {
props.setStatus(status)
props.setStatus(status);
}
} catch (err) {
app.$notify({
group: 'main',
title: 'Error sending message',
group: "main",
title: "Error sending message",
text: err.data ? err.data.description : err,
type: 'error',
})
type: "error",
});
}
}
async function closeReport(reply) {
if (reply) {
await sendReply()
await sendReply();
}
try {
await useBaseFetch(`report/${props.report.id}`, {
method: 'PATCH',
method: "PATCH",
body: {
closed: true,
},
})
await updateThreadLocal()
});
await updateThreadLocal();
} catch (err) {
app.$notify({
group: 'main',
title: 'Error closing report',
group: "main",
title: "Error closing report",
text: err.data ? err.data.description : err,
type: 'error',
})
type: "error",
});
}
}
async function reopenReport() {
try {
await useBaseFetch(`report/${props.report.id}`, {
method: 'PATCH',
method: "PATCH",
body: {
closed: false,
},
})
await updateThreadLocal()
});
await updateThreadLocal();
} catch (err) {
app.$notify({
group: 'main',
title: 'Error reopening report',
group: "main",
title: "Error reopening report",
text: err.data ? err.data.description : err,
type: 'error',
})
type: "error",
});
}
}
const replyWithSubmission = ref(false)
const submissionConfirmation = ref(false)
const replyWithSubmission = ref(false);
const submissionConfirmation = ref(false);
function openResubmitModal(reply) {
submissionConfirmation.value = false
replyWithSubmission.value = reply
modalSubmit.value.show()
submissionConfirmation.value = false;
replyWithSubmission.value = reply;
modalSubmit.value.show();
}
async function resubmit() {
if (replyWithSubmission.value) {
await sendReply('processing')
await sendReply("processing");
} else {
await props.setStatus('processing')
await props.setStatus("processing");
}
modalSubmit.value.hide()
modalSubmit.value.hide();
}
const requestedStatus = computed(() => props.project.requested_status ?? 'approved')
const requestedStatus = computed(() => props.project.requested_status ?? "approved");
</script>
<style lang="scss" scoped>

View File

@@ -106,12 +106,12 @@ import {
LockIcon,
ModrinthIcon,
ScaleIcon,
} from '@modrinth/assets'
import { OverflowMenu, ConditionalNuxtLink } from '@modrinth/ui'
import { renderString } from '@modrinth/utils'
import Avatar from '~/components/ui/Avatar.vue'
import Badge from '~/components/ui/Badge.vue'
import { isStaff } from '~/helpers/users.js'
} from "@modrinth/assets";
import { OverflowMenu, ConditionalNuxtLink } from "@modrinth/ui";
import { renderString } from "@modrinth/utils";
import Avatar from "~/components/ui/Avatar.vue";
import Badge from "~/components/ui/Badge.vue";
import { isStaff } from "~/helpers/users.js";
const props = defineProps({
message: {
@@ -142,34 +142,34 @@ const props = defineProps({
type: Object,
required: true,
},
})
});
const emit = defineEmits(['update-thread'])
const emit = defineEmits(["update-thread"]);
const formattedMessage = computed(() => {
const body = renderString(props.message.body.body)
const body = renderString(props.message.body.body);
if (props.forceCompact) {
const hasImage = body.includes('<img')
const noHtml = body.replace(/<\/?[^>]+(>|$)/g, '')
const hasImage = body.includes("<img");
const noHtml = body.replace(/<\/?[^>]+(>|$)/g, "");
if (noHtml.trim()) {
return noHtml
return noHtml;
} else if (hasImage) {
return 'sent an image.'
return "sent an image.";
} else {
return 'sent a message.'
return "sent a message.";
}
}
return body
})
return body;
});
const formatRelativeTime = useRelativeTime()
const timeSincePosted = ref(formatRelativeTime(props.message.created))
const formatRelativeTime = useRelativeTime();
const timeSincePosted = ref(formatRelativeTime(props.message.created));
async function deleteMessage() {
await useBaseFetch(`message/${props.message.id}`, {
method: 'DELETE',
})
emit('update-thread')
method: "DELETE",
});
emit("update-thread");
}
</script>
@@ -194,9 +194,9 @@ async function deleteMessage() {
--gap-size: var(--spacing-card-sm);
display: grid;
grid-template:
'icon author actions'
'icon body actions'
'date date date';
"icon author actions"
"icon body actions"
"date date date";
grid-template-columns: min-content auto 1fr;
column-gap: var(--gap-size);
row-gap: var(--spacing-card-xs);
@@ -312,9 +312,9 @@ role-moderator {
&.has-body {
grid-template:
'icon author actions'
'icon body actions'
'date date date';
"icon author actions"
"icon body actions"
"date date date";
grid-template-columns: min-content auto 1fr;
}
}
@@ -327,8 +327,8 @@ role-moderator {
&.has-body {
grid-template:
'icon author date actions'
'icon body body actions';
"icon author date actions"
"icon body body actions";
grid-template-columns: min-content auto 1fr;
grid-template-rows: min-content 1fr auto;
}

View File

@@ -24,8 +24,8 @@
</template>
<script setup>
import ChevronRightIcon from '~/assets/images/utils/chevron-right.svg?component'
import ThreadMessage from '~/components/ui/thread/ThreadMessage.vue'
import ChevronRightIcon from "~/assets/images/utils/chevron-right.svg?component";
import ThreadMessage from "~/components/ui/thread/ThreadMessage.vue";
const props = defineProps({
thread: {
@@ -49,36 +49,36 @@ const props = defineProps({
type: Array,
required: false,
default() {
return []
return [];
},
},
auth: {
type: Object,
required: true,
},
})
});
const app = useNuxtApp()
const app = useNuxtApp();
const members = computed(() => {
const members = {}
const members = {};
for (const member of props.thread.members) {
members[member.id] = member
members[member.id] = member;
}
members[props.auth.user.id] = props.auth.user
return members
})
members[props.auth.user.id] = props.auth.user;
return members;
});
const displayMessages = computed(() => {
const sortedMessages = props.thread.messages
.slice()
.sort((a, b) => app.$dayjs(a.created) - app.$dayjs(b.created))
.sort((a, b) => app.$dayjs(a.created) - app.$dayjs(b.created));
if (props.messages.length > 0) {
return sortedMessages.filter((msg) => props.messages.includes(msg.id))
return sortedMessages.filter((msg) => props.messages.includes(msg.id));
} else {
return sortedMessages.length > 0 ? [sortedMessages[sortedMessages.length - 1]] : []
return sortedMessages.length > 0 ? [sortedMessages[sortedMessages.length - 1]] : [];
}
})
});
</script>
<style lang="scss" scoped>