separate sort order component

This commit is contained in:
samunohito 2024-02-27 21:00:03 +09:00
parent 47ca0e6689
commit cb136e635d
6 changed files with 175 additions and 240 deletions

View File

@ -5,6 +5,26 @@ export type RequestLogItem = {
error?: string; error?: string;
}; };
export const gridSortOrderKeys = [
'name',
'category',
'aliases',
'type',
'license',
'host',
'uri',
'publicUrl',
'isSensitive',
'localOnly',
'updatedAt',
];
export type GridSortOrderKey = typeof gridSortOrderKeys[number];
export type GridSortOrder = {
key: GridSortOrderKey;
direction: 'ASC' | 'DESC';
}
export function emptyStrToUndefined(value: string | null) { export function emptyStrToUndefined(value: string | null) {
return value ? value : undefined; return value ? value : undefined;
} }

View File

@ -118,26 +118,7 @@
</MkInput> </MkInput>
</div> </div>
<MkFolder :spacerMax="8" :spacerMin="8"> <XSortOrderFolder :sortOrders="sortOrders" @update="onSortOrderUpdate"/>
<template #icon><i class="ti ti-arrows-sort"></i></template>
<template #label>{{ i18n.ts._customEmojisManager._gridCommon.sortOrder }}</template>
<div :class="$style.sortOrderArea">
<div :class="$style.sortOrderAreaTags">
<MkTagItem
v-for="order in sortOrders"
:key="order.key"
:iconClass="order.direction === 'ASC' ? 'ti ti-arrow-up' : 'ti ti-arrow-down'"
:exButtonIconClass="'ti ti-x'"
:content="order.key"
@click="onToggleSortOrderButtonClicked(order)"
@exButtonClick="onRemoveSortOrderButtonClicked(order.key)"
/>
</div>
<MkButton :class="$style.sortOrderAddButton" @click="onAddSortOrderButtonClicked">
<span class="ti ti-plus"/>
</MkButton>
</div>
</MkFolder>
<div :class="[[spMode ? $style.searchButtonsSp : $style.searchButtons]]"> <div :class="[[spMode ? $style.searchButtonsSp : $style.searchButtons]]">
<MkButton primary @click="onSearchRequest"> <MkButton primary @click="onSearchRequest">
@ -150,15 +131,7 @@
</div> </div>
</MkFolder> </MkFolder>
<MkFolder> <XRegisterLogsFolder :logs="requestLogs"/>
<template #icon><i class="ti ti-notes"></i></template>
<template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template>
<template #caption>
{{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }}
</template>
<XRegisterLogs :logs="requestLogs"/>
</MkFolder>
<div :class="$style.gridArea"> <div :class="$style.gridArea">
<MkGrid :data="gridItems" :settings="setupGrid()" @event="onGridEvent"/> <MkGrid :data="gridItems" :settings="setupGrid()" @event="onGridEvent"/>
@ -188,7 +161,7 @@ import * as os from '@/os.js';
import { import {
emptyStrToEmptyArray, emptyStrToEmptyArray,
emptyStrToNull, emptyStrToNull,
emptyStrToUndefined, emptyStrToUndefined, GridSortOrder,
RequestLogItem, RequestLogItem,
roleIdsParser, roleIdsParser,
} from '@/pages/admin/custom-emojis-manager.impl.js'; } from '@/pages/admin/custom-emojis-manager.impl.js';
@ -200,13 +173,12 @@ import { validators } from '@/components/grid/cell-validators.js';
import { GridCellValidationEvent, GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js'; import { GridCellValidationEvent, GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
import { misskeyApi } from '@/scripts/misskey-api.js'; import { misskeyApi } from '@/scripts/misskey-api.js';
import MkPagingButtons from '@/components/MkPagingButtons.vue'; import MkPagingButtons from '@/components/MkPagingButtons.vue';
import XRegisterLogs from '@/pages/admin/custom-emojis-manager.logs.vue'; import XRegisterLogsFolder from '@/pages/admin/custom-emojis-manager.logs-folder.vue';
import XSortOrderFolder from '@/pages/admin/custom-emojis-manager.sort-order-folder.vue';
import MkFolder from '@/components/MkFolder.vue'; import MkFolder from '@/components/MkFolder.vue';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
import { deviceKind } from '@/scripts/device-kind.js'; import { deviceKind } from '@/scripts/device-kind.js';
import { GridSetting } from '@/components/grid/grid.js'; import { GridSetting } from '@/components/grid/grid.js';
import MkTagItem from '@/components/MkTagItem.vue';
import { MenuItem } from '@/types/menu.js';
import { selectFile } from '@/scripts/select-file.js'; import { selectFile } from '@/scripts/select-file.js';
import { copyGridDataToClipboard, removeDataFromGrid } from '@/components/grid/grid-utils.js'; import { copyGridDataToClipboard, removeDataFromGrid } from '@/components/grid/grid-utils.js';
@ -228,23 +200,6 @@ type GridItem = {
originalUrl?: string | null; originalUrl?: string | null;
} }
const gridSortOrderKeys = [
'name',
'category',
'aliases',
'type',
'license',
'isSensitive',
'localOnly',
'updatedAt',
];
type GridSortOrderKey = typeof gridSortOrderKeys[number];
type GridSortOrder = {
key: GridSortOrderKey;
direction: 'ASC' | 'DESC';
}
function setupGrid(): GridSetting { function setupGrid(): GridSetting {
const required = validators.required(); const required = validators.required();
const regex = validators.regex(/^[a-zA-Z0-9_]+$/); const regex = validators.regex(/^[a-zA-Z0-9_]+$/);
@ -538,33 +493,8 @@ async function onQueryRolesEditClicked() {
queryRoles.value = result.result; queryRoles.value = result.result;
} }
function onToggleSortOrderButtonClicked(order: GridSortOrder) { function onSortOrderUpdate(_sortOrders: GridSortOrder[]) {
switch (order.direction) { sortOrders.value = _sortOrders;
case 'ASC':
order.direction = 'DESC';
break;
case 'DESC':
order.direction = 'ASC';
break;
}
}
function onRemoveSortOrderButtonClicked(key: GridSortOrderKey) {
sortOrders.value = sortOrders.value.filter(it => it.key !== key);
}
function onAddSortOrderButtonClicked(ev: MouseEvent) {
const menuItems: MenuItem[] = gridSortOrderKeys
.filter(key => !sortOrders.value.map(it => it.key).includes(key))
.map(it => {
return {
text: it,
action: () => {
sortOrders.value.push({ key: it, direction: 'ASC' });
},
};
});
os.contextMenu(menuItems, ev);
} }
async function onSearchRequest() { async function onSearchRequest() {
@ -751,37 +681,6 @@ onMounted(async () => {
gap: 8px; gap: 8px;
} }
.sortOrderArea {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
}
.sortOrderAreaTags {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
flex-wrap: wrap;
gap: 8px;
}
.sortOrderAddButton {
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
min-width: 2.0em;
min-height: 2.0em;
max-width: 2.0em;
max-height: 2.0em;
padding: 8px;
margin-left: auto;
border-radius: 9999px;
background-color: var(--buttonBg);
}
.gridArea { .gridArea {
overflow: scroll; overflow: scroll;
padding-top: 8px; padding-top: 8px;

View File

@ -25,15 +25,7 @@
</div> </div>
</MkFolder> </MkFolder>
<MkFolder> <XRegisterLogsFolder :logs="requestLogs"/>
<template #icon><i class="ti ti-notes"></i></template>
<template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template>
<template #caption>
{{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }}
</template>
<XRegisterLogs :logs="requestLogs"/>
</MkFolder>
<div <div
:class="[$style.uploadBox, [isDragOver ? $style.dragOver : {}]]" :class="[$style.uploadBox, [isDragOver ? $style.dragOver : {}]]"
@ -94,7 +86,7 @@ import { chooseFileFromDrive, chooseFileFromPc } from '@/scripts/select-file.js'
import { uploadFile } from '@/scripts/upload.js'; import { uploadFile } from '@/scripts/upload.js';
import { GridCellValidationEvent, GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js'; import { GridCellValidationEvent, GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
import { DroppedFile, extractDroppedItems, flattenDroppedFiles } from '@/scripts/file-drop.js'; import { DroppedFile, extractDroppedItems, flattenDroppedFiles } from '@/scripts/file-drop.js';
import XRegisterLogs from '@/pages/admin/custom-emojis-manager.logs.vue'; import XRegisterLogsFolder from '@/pages/admin/custom-emojis-manager.logs-folder.vue';
import { GridSetting } from '@/components/grid/grid.js'; import { GridSetting } from '@/components/grid/grid.js';
import { copyGridDataToClipboard } from '@/components/grid/grid-utils.js'; import { copyGridDataToClipboard } from '@/components/grid/grid-utils.js';
import { GridRow } from '@/components/grid/row.js'; import { GridRow } from '@/components/grid/row.js';

View File

@ -1,25 +1,33 @@
<template> <template>
<div> <MkFolder>
<div v-if="logs.length > 0" style="display:flex; flex-direction: column; overflow-y: scroll; gap: 16px;"> <template #icon><i class="ti ti-notes"></i></template>
<MkSwitch v-model="showingSuccessLogs"> <template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template>
<template #label>{{ i18n.ts._customEmojisManager._logs.showSuccessLogSwitch }}</template> <template #caption>
</MkSwitch> {{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }}
<div> </template>
<div v-if="filteredLogs.length > 0">
<MkGrid <div>
:data="filteredLogs" <div v-if="logs.length > 0" style="display:flex; flex-direction: column; overflow-y: scroll; gap: 16px;">
:settings="setupGrid()" <MkSwitch v-model="showingSuccessLogs">
/> <template #label>{{ i18n.ts._customEmojisManager._logs.showSuccessLogSwitch }}</template>
</div> </MkSwitch>
<div v-else> <div>
{{ i18n.ts._customEmojisManager._logs.failureLogNothing }} <div v-if="filteredLogs.length > 0">
<MkGrid
:data="filteredLogs"
:settings="setupGrid()"
/>
</div>
<div v-else>
{{ i18n.ts._customEmojisManager._logs.failureLogNothing }}
</div>
</div> </div>
</div> </div>
<div v-else>
{{ i18n.ts._customEmojisManager._logs.logNothing }}
</div>
</div> </div>
<div v-else> </MkFolder>
{{ i18n.ts._customEmojisManager._logs.logNothing }}
</div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -31,6 +39,7 @@ import MkGrid from '@/components/grid/MkGrid.vue';
import MkSwitch from '@/components/MkSwitch.vue'; import MkSwitch from '@/components/MkSwitch.vue';
import { GridSetting } from '@/components/grid/grid.js'; import { GridSetting } from '@/components/grid/grid.js';
import { copyGridDataToClipboard } from '@/components/grid/grid-utils.js'; import { copyGridDataToClipboard } from '@/components/grid/grid-utils.js';
import MkFolder from '@/components/MkFolder.vue';
function setupGrid(): GridSetting { function setupGrid(): GridSetting {
return { return {

View File

@ -52,26 +52,7 @@
</MkInput> </MkInput>
</div> </div>
<MkFolder :spacerMax="8" :spacerMin="8"> <XSortOrderFolder :sortOrders="sortOrders" @update="onSortOrderChanged"/>
<template #icon><i class="ti ti-arrows-sort"></i></template>
<template #label>{{ i18n.ts._customEmojisManager._gridCommon.sortOrder }}</template>
<div :class="$style.sortOrderArea">
<div :class="$style.sortOrderAreaTags">
<MkTagItem
v-for="order in sortOrders"
:key="order.key"
:iconClass="order.direction === 'ASC' ? 'ti ti-arrow-up' : 'ti ti-arrow-down'"
:exButtonIconClass="'ti ti-x'"
:content="order.key"
@click="onToggleSortOrderButtonClicked(order)"
@exButtonClick="onRemoveSortOrderButtonClicked(order.key)"
/>
</div>
<MkButton :class="$style.sortOrderAddButton" @click="onAddSortOrderButtonClicked">
<span class="ti ti-plus"/>
</MkButton>
</div>
</MkFolder>
<div :class="[[spMode ? $style.searchButtonsSp : $style.searchButtons]]"> <div :class="[[spMode ? $style.searchButtonsSp : $style.searchButtons]]">
<MkButton primary @click="onSearchRequest"> <MkButton primary @click="onSearchRequest">
@ -84,15 +65,7 @@
</div> </div>
</MkFolder> </MkFolder>
<MkFolder> <XRegisterLogsFolder :logs="requestLogs"/>
<template #icon><i class="ti ti-notes"></i></template>
<template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template>
<template #caption>
{{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }}
</template>
<XRegisterLogs :logs="requestLogs"/>
</MkFolder>
<div v-if="gridItems.length > 0" :class="$style.gridArea"> <div v-if="gridItems.length > 0" :class="$style.gridArea">
<MkGrid :data="gridItems" :settings="setupGrid()" @event="onGridEvent"/> <MkGrid :data="gridItems" :settings="setupGrid()" @event="onGridEvent"/>
@ -115,15 +88,14 @@ import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkGrid from '@/components/grid/MkGrid.vue'; import MkGrid from '@/components/grid/MkGrid.vue';
import { emptyStrToUndefined, RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js'; import { emptyStrToUndefined, GridSortOrder, RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js';
import { GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js'; import { GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js';
import MkFolder from '@/components/MkFolder.vue'; import MkFolder from '@/components/MkFolder.vue';
import XRegisterLogs from '@/pages/admin/custom-emojis-manager.logs.vue'; import XRegisterLogsFolder from '@/pages/admin/custom-emojis-manager.logs-folder.vue';
import XSortOrderFolder from '@/pages/admin/custom-emojis-manager.sort-order-folder.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { GridSetting } from '@/components/grid/grid.js'; import { GridSetting } from '@/components/grid/grid.js';
import MkTagItem from '@/components/MkTagItem.vue';
import { deviceKind } from '@/scripts/device-kind.js'; import { deviceKind } from '@/scripts/device-kind.js';
import { MenuItem } from '@/types/menu.js';
import MkPagingButtons from '@/components/MkPagingButtons.vue'; import MkPagingButtons from '@/components/MkPagingButtons.vue';
type GridItem = { type GridItem = {
@ -134,19 +106,6 @@ type GridItem = {
host: string; host: string;
} }
const gridSortOrderKeys = [
'name',
'host',
'uri',
'publicUrl',
];
type GridSortOrderKey = typeof gridSortOrderKeys[number];
type GridSortOrder = {
key: GridSortOrderKey;
direction: 'ASC' | 'DESC';
}
function setupGrid(): GridSetting { function setupGrid(): GridSetting {
return { return {
row: { row: {
@ -206,33 +165,8 @@ const gridItems = ref<GridItem[]>([]);
const spMode = computed(() => ['smartphone', 'tablet'].includes(deviceKind)); const spMode = computed(() => ['smartphone', 'tablet'].includes(deviceKind));
function onToggleSortOrderButtonClicked(order: GridSortOrder) { function onSortOrderChanged(_sortOrders: GridSortOrder[]) {
switch (order.direction) { sortOrders.value = _sortOrders;
case 'ASC':
order.direction = 'DESC';
break;
case 'DESC':
order.direction = 'ASC';
break;
}
}
function onRemoveSortOrderButtonClicked(key: GridSortOrderKey) {
sortOrders.value = sortOrders.value.filter(it => it.key !== key);
}
function onAddSortOrderButtonClicked(ev: MouseEvent) {
const menuItems: MenuItem[] = gridSortOrderKeys
.filter(key => !sortOrders.value.map(it => it.key).includes(key))
.map(it => {
return {
text: it,
action: () => {
sortOrders.value.push({ key: it, direction: 'ASC' });
},
};
});
os.contextMenu(menuItems, ev);
} }
async function onSearchRequest() { async function onSearchRequest() {
@ -412,37 +346,6 @@ onMounted(async () => {
gap: 8px; gap: 8px;
} }
.sortOrderArea {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
}
.sortOrderAreaTags {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
flex-wrap: wrap;
gap: 8px;
}
.sortOrderAddButton {
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
min-width: 2.0em;
min-height: 2.0em;
max-width: 2.0em;
max-height: 2.0em;
padding: 8px;
margin-left: auto;
border-radius: 9999px;
background-color: var(--buttonBg);
}
.gridArea { .gridArea {
overflow: scroll; overflow: scroll;
padding-top: 8px; padding-top: 8px;

View File

@ -0,0 +1,112 @@
<template>
<MkFolder :spacerMax="8" :spacerMin="8">
<template #icon><i class="ti ti-arrows-sort"></i></template>
<template #label>{{ i18n.ts._customEmojisManager._gridCommon.sortOrder }}</template>
<div :class="$style.sortOrderArea">
<div :class="$style.sortOrderAreaTags">
<MkTagItem
v-for="order in sortOrders"
:key="order.key"
:iconClass="order.direction === 'ASC' ? 'ti ti-arrow-up' : 'ti ti-arrow-down'"
:exButtonIconClass="'ti ti-x'"
:content="order.key"
@click="onToggleSortOrderButtonClicked(order)"
@exButtonClick="onRemoveSortOrderButtonClicked(order.key)"
/>
</div>
<MkButton :class="$style.sortOrderAddButton" @click="onAddSortOrderButtonClicked">
<span class="ti ti-plus"/>
</MkButton>
</div>
</MkFolder>
</template>
<script setup lang="ts">
import { toRefs } from 'vue';
import { i18n } from '@/i18n.js';
import MkFolder from '@/components/MkFolder.vue';
import MkTagItem from '@/components/MkTagItem.vue';
import MkButton from '@/components/MkButton.vue';
import { GridSortOrder, GridSortOrderKey, gridSortOrderKeys } from '@/pages/admin/custom-emojis-manager.impl.js';
import { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js';
const emit = defineEmits<{
(ev: 'update', sortOrders: GridSortOrder[]): void;
}>();
const props = defineProps<{
sortOrders: GridSortOrder[];
}>();
const { sortOrders } = toRefs(props);
function onToggleSortOrderButtonClicked(order: GridSortOrder) {
switch (order.direction) {
case 'ASC':
order.direction = 'DESC';
break;
case 'DESC':
order.direction = 'ASC';
break;
}
emitOrder(sortOrders.value);
}
function onAddSortOrderButtonClicked(ev: MouseEvent) {
const menuItems: MenuItem[] = gridSortOrderKeys
.filter(key => !sortOrders.value.map(it => it.key).includes(key))
.map(it => {
return {
text: it,
action: () => {
emitOrder([...sortOrders.value, { key: it, direction: 'ASC' }]);
},
};
});
os.contextMenu(menuItems, ev);
}
function onRemoveSortOrderButtonClicked(key: GridSortOrderKey) {
emitOrder(sortOrders.value.filter(it => it.key !== key));
}
function emitOrder(sortOrders: GridSortOrder[]) {
emit('update', sortOrders);
}
</script>
<style module lang="scss">
.sortOrderArea {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
}
.sortOrderAreaTags {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
flex-wrap: wrap;
gap: 8px;
}
.sortOrderAddButton {
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
min-width: 2.0em;
min-height: 2.0em;
max-width: 2.0em;
max-height: 2.0em;
padding: 8px;
margin-left: auto;
border-radius: 9999px;
background-color: var(--buttonBg);
}
</style>