refine context menu setting

This commit is contained in:
samunohito 2024-02-16 07:41:29 +09:00
parent 369d5971d4
commit c0f941689b
11 changed files with 243 additions and 255 deletions

View file

@ -48,7 +48,7 @@ import { CELL_ADDRESS_NONE, CellAddress, CellValue, createCell, GridCell, resetC
import { equalCellAddress, getCellAddress, getCellElement } from '@/components/grid/grid-utils.js';
import { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js';
import { GridCurrentState, GridEvent } from '@/components/grid/grid-event.js';
import { GridContext, GridEvent } from '@/components/grid/grid-event.js';
import { createColumn, GridColumn } from '@/components/grid/column.js';
import { createRow, defaultGridRowSetting, GridRow, GridRowSetting, resetRow } from '@/components/grid/row.js';
@ -59,7 +59,7 @@ type RowHolder = {
}
const emit = defineEmits<{
(ev: 'event', event: GridEvent, current: GridCurrentState): void;
(ev: 'event', event: GridEvent, current: GridContext): void;
}>();
const props = defineProps<{
@ -76,6 +76,9 @@ const rowSetting: Required<GridRowSetting> = {
// non-reactive
const columnSettings = props.settings.cols;
// non-reactive
const cellSettings = props.settings.cells ?? {};
const { data } = toRefs(props);
// #region Event Definitions
@ -700,15 +703,30 @@ function onContextMenu(ev: MouseEvent) {
console.log(`[grid][context-menu] button: ${ev.button}, cell: ${cellAddress.row}x${cellAddress.col}`);
}
const context = createContext();
const menuItems = Array.of<MenuItem>();
//
if (availableCellAddress(cellAddress)) {
emitGridEvent({ type: 'cell-context-menu', event: ev, menuItems });
} else if (isRowNumberCellAddress(cellAddress)) {
emitGridEvent({ type: 'row-context-menu', event: ev, menuItems });
} else if (isColumnHeaderCellAddress(cellAddress)) {
emitGridEvent({ type: 'column-context-menu', event: ev, menuItems });
switch (true) {
case availableCellAddress(cellAddress): {
const cell = cells.value[cellAddress.row].cells[cellAddress.col];
if (cell.setting.contextMenuFactory) {
menuItems.push(...cell.setting.contextMenuFactory(cell.column, cell.row, cell.value, context));
}
break;
}
case isColumnHeaderCellAddress(cellAddress): {
const col = columns.value[cellAddress.col];
if (col.setting.contextMenuFactory) {
menuItems.push(...col.setting.contextMenuFactory(col, context));
}
break;
}
case isRowNumberCellAddress(cellAddress): {
const row = rows.value[cellAddress.row];
if (row.setting.contextMenuFactory) {
menuItems.push(...row.setting.contextMenuFactory(row, context));
}
break;
}
}
if (menuItems.length > 0) {
@ -835,7 +853,7 @@ function calcLargestCellWidth(column: GridColumn) {
* {@link emit}を使用してイベントを発行する
*/
function emitGridEvent(ev: GridEvent) {
const currentState: GridCurrentState = {
const currentState: GridContext = {
selectedCell: selectedCell.value,
rangedCells: rangedCells.value,
rangedRows: rangedRows.value,
@ -1037,6 +1055,19 @@ function unregisterMouseUp() {
removeEventListener('mouseup', onMouseUp);
}
function createContext(): GridContext {
return {
selectedCell: selectedCell.value,
rangedCells: rangedCells.value,
rangedRows: rangedRows.value,
randedBounds: rangedBounds.value,
availableBounds: availableBounds.value,
state: state.value,
rows: rows.value,
columns: columns.value,
};
}
function refreshData() {
if (_DEV_) {
console.log('[grid][refresh-data][begin]');
@ -1044,8 +1075,8 @@ function refreshData() {
const _data: DataSource[] = data.value;
const _rows: GridRow[] = (_data.length > rowSetting.minimumDefinitionCount)
? _data.map((_, index) => createRow(index, true))
: Array.from({ length: rowSetting.minimumDefinitionCount }, (_, index) => createRow(index, index < _data.length));
? _data.map((_, index) => createRow(index, true, rowSetting))
: Array.from({ length: rowSetting.minimumDefinitionCount }, (_, index) => createRow(index, index < _data.length, rowSetting));
const _cols: GridColumn[] = columns.value;
//
@ -1053,11 +1084,11 @@ function refreshData() {
const _cells: RowHolder[] = _rows.map(row => {
const cells = row.using
? _cols.map(col => {
const cell = createCell(col, row, _data[row.index][col.setting.bindTo]);
const cell = createCell(col, row, _data[row.index][col.setting.bindTo], cellSettings);
cell.violation = cellValidation(cell, cell.value);
return cell;
})
: _cols.map(col => createCell(col, row, undefined));
: _cols.map(col => createCell(col, row, undefined, cellSettings));
return { row, cells, origin: _data[row.index] };
});
@ -1095,11 +1126,11 @@ function patchData(newItems: DataSource[]) {
//
for (let rowIdx = rows.value.length; rowIdx < newItems.length; rowIdx++) {
const newRow = createRow(rowIdx, true);
const newRow = createRow(rowIdx, true, rowSetting);
newRows.push(newRow);
newCells.push({
row: newRow,
cells: _cols.map(col => createCell(col, newRow, newItems[rowIdx][col.setting.bindTo], col.setting.cellSetting ?? {})),
cells: _cols.map(col => createCell(col, newRow, newItems[rowIdx][col.setting.bindTo], cellSettings)),
origin: newItems[rowIdx],
});
}

View file

@ -2,6 +2,8 @@ import { ValidateViolation } from '@/components/grid/cell-validators.js';
import { Size } from '@/components/grid/grid.js';
import { GridColumn } from '@/components/grid/column.js';
import { GridRow } from '@/components/grid/row.js';
import { MenuItem } from '@/types/menu.js';
import { GridContext } from '@/components/grid/grid-event.js';
export type CellValue = string | boolean | number | undefined | null
@ -23,13 +25,21 @@ export type GridCell = {
selected: boolean;
ranged: boolean;
contentSize: Size;
setting: GridCellSetting;
violation: ValidateViolation;
}
export type GridCellContextMenuFactory = (col: GridColumn, row: GridRow, value: CellValue, context: GridContext) => MenuItem[];
export type GridCellSetting = {
contextMenuFactory?: GridCellContextMenuFactory;
}
export function createCell(
column: GridColumn,
row: GridRow,
value: CellValue,
setting: GridCellSetting,
): GridCell {
return {
address: { row: row.index, col: column.index },
@ -48,6 +58,7 @@ export function createCell(
},
violations: [],
},
setting,
};
}

View file

@ -1,9 +1,16 @@
import { CellValidator } from '@/components/grid/cell-validators.js';
import { Size, SizeStyle } from '@/components/grid/grid.js';
import { calcCellWidth } from '@/components/grid/grid-utils.js';
import { CellValue } from '@/components/grid/cell.js';
import { GridRow } from '@/components/grid/row.js';
import { MenuItem } from '@/types/menu.js';
import { GridContext } from '@/components/grid/grid-event.js';
export type ColumnType = 'text' | 'number' | 'date' | 'boolean' | 'image' | 'hidden';
export type CellValueConverter = (row: GridRow, col: GridColumn, value: CellValue) => CellValue;
export type GridColumnContextMenuFactory = (col: GridColumn, context: GridContext) => MenuItem[];
export type GridColumnSetting = {
bindTo: string;
title?: string;
@ -12,6 +19,8 @@ export type GridColumnSetting = {
width: SizeStyle;
editable?: boolean;
validators?: CellValidator[];
valueConverter?: CellValueConverter;
contextMenuFactory?: GridColumnContextMenuFactory;
};
export type GridColumn = {

View file

@ -1,11 +1,10 @@
import { CellAddress, CellValue, GridCell } from '@/components/grid/cell.js';
import { GridState } from '@/components/grid/grid.js';
import { ValidateViolation } from '@/components/grid/cell-validators.js';
import { MenuItem } from '@/types/menu.js';
import { GridColumn } from '@/components/grid/column.js';
import { GridRow } from '@/components/grid/row.js';
export type GridCurrentState = {
export type GridContext = {
selectedCell?: GridCell;
rangedCells: GridCell[];
rangedRows: GridRow[];
@ -26,10 +25,7 @@ export type GridEvent =
GridCellValueChangeEvent |
GridKeyDownEvent |
GridMouseDownEvent |
GridCellValidationEvent |
GridCellContextMenuEvent |
GridRowContextMenuEvent |
GridColumnContextMenuEvent
GridCellValidationEvent
;
export type GridCellValueChangeEvent = {
@ -57,21 +53,3 @@ export type GridMouseDownEvent = {
event: MouseEvent;
clickedCellAddress: CellAddress;
};
export type GridCellContextMenuEvent = {
type: 'cell-context-menu';
event: MouseEvent;
menuItems: MenuItem[];
};
export type GridRowContextMenuEvent = {
type: 'row-context-menu';
event: MouseEvent;
menuItems: MenuItem[];
};
export type GridColumnContextMenuEvent = {
type: 'column-context-menu';
event: MouseEvent;
menuItems: MenuItem[];
};

View file

@ -1,11 +1,12 @@
import { EventEmitter } from 'eventemitter3';
import { CellValue } from '@/components/grid/cell.js';
import { CellValue, GridCellSetting } from '@/components/grid/cell.js';
import { GridColumnSetting } from '@/components/grid/column.js';
import { GridRowSetting } from '@/components/grid/row.js';
export type GridSetting = {
row?: GridRowSetting;
cols: GridColumnSetting[];
cells?: GridCellSetting;
};
export type DataSource = Record<string, CellValue>;

View file

@ -1,12 +1,12 @@
import { Ref } from 'vue';
import { GridCurrentState, GridKeyDownEvent } from '@/components/grid/grid-event.js';
import { GridContext, GridKeyDownEvent } from '@/components/grid/grid-event.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { GridColumnSetting } from '@/components/grid/column.js';
import { CellValue } from '@/components/grid/cell.js';
import { DataSource } from '@/components/grid/grid.js';
class OptInGridUtils {
async defaultKeyDownHandler(gridItems: Ref<DataSource[]>, event: GridKeyDownEvent, currentState: GridCurrentState) {
async defaultKeyDownHandler(gridItems: Ref<DataSource[]>, event: GridKeyDownEvent, context: GridContext) {
const { ctrlKey, shiftKey, code } = event.event;
switch (true) {
@ -16,11 +16,11 @@ class OptInGridUtils {
case ctrlKey: {
switch (code) {
case 'KeyC': {
this.copyToClipboard(gridItems, currentState);
this.copyToClipboard(gridItems, context);
break;
}
case 'KeyV': {
await this.pasteFromClipboard(gridItems, currentState);
await this.pasteFromClipboard(gridItems, context);
break;
}
}
@ -32,7 +32,7 @@ class OptInGridUtils {
default: {
switch (code) {
case 'Delete': {
this.deleteSelectionRange(gridItems, currentState);
this.deleteSelectionRange(gridItems, context);
break;
}
}
@ -41,14 +41,14 @@ class OptInGridUtils {
}
}
copyToClipboard(gridItems: Ref<DataSource[]>, currentState: GridCurrentState) {
copyToClipboard(gridItems: Ref<DataSource[]>, context: GridContext) {
const lines = Array.of<string>();
const bounds = currentState.randedBounds;
const bounds = context.randedBounds;
for (let row = bounds.leftTop.row; row <= bounds.rightBottom.row; row++) {
const items = Array.of<string>();
for (let col = bounds.leftTop.col; col <= bounds.rightBottom.col; col++) {
const bindTo = currentState.columns[col].setting.bindTo;
const bindTo = context.columns[col].setting.bindTo;
const cell = gridItems.value[row][bindTo];
items.push(cell?.toString() ?? '');
}
@ -65,7 +65,7 @@ class OptInGridUtils {
async pasteFromClipboard(
gridItems: Ref<DataSource[]>,
currentState: GridCurrentState,
context: GridContext,
) {
function parseValue(value: string, type: GridColumnSetting['type']): CellValue {
switch (type) {
@ -86,14 +86,14 @@ class OptInGridUtils {
console.log(`Paste from clipboard: ${clipBoardText}`);
}
const bounds = currentState.randedBounds;
const bounds = context.randedBounds;
const lines = clipBoardText.replace(/\r/g, '')
.split('\n')
.map(it => it.split('\t'));
if (lines.length === 1 && lines[0].length === 1) {
// 単独文字列の場合は選択範囲全体に同じテキストを貼り付ける
const ranges = currentState.rangedCells;
const ranges = context.rangedCells;
for (const cell of ranges) {
gridItems.value[cell.row.index][cell.column.setting.bindTo] = parseValue(lines[0][0], cell.column.setting.type);
}
@ -101,7 +101,7 @@ class OptInGridUtils {
// 表形式文字列の場合は表形式にパースし、選択範囲に合うように貼り付ける
const offsetRow = bounds.leftTop.row;
const offsetCol = bounds.leftTop.col;
const columns = currentState.columns;
const columns = context.columns;
for (let row = bounds.leftTop.row; row <= bounds.rightBottom.row; row++) {
const rowIdx = row - offsetRow;
if (lines.length <= rowIdx) {
@ -123,12 +123,12 @@ class OptInGridUtils {
}
}
deleteSelectionRange(gridItems: Ref<DataSource[]>, currentState: GridCurrentState) {
if (currentState.rangedRows.length > 0) {
const deletedIndexes = currentState.rangedRows.map(it => it.index);
deleteSelectionRange(gridItems: Ref<DataSource[]>, context: GridContext) {
if (context.rangedRows.length > 0) {
const deletedIndexes = context.rangedRows.map(it => it.index);
gridItems.value = gridItems.value.filter((_, index) => !deletedIndexes.includes(index));
} else {
const ranges = currentState.rangedCells;
const ranges = context.rangedCells;
for (const cell of ranges) {
if (cell.column.setting.editable) {
gridItems.value[cell.row.index][cell.column.setting.bindTo] = undefined;

View file

@ -1,12 +1,15 @@
import { AdditionalStyle } from '@/components/grid/grid.js';
import { GridCell } from '@/components/grid/cell.js';
import { GridColumn } from '@/components/grid/column.js';
import { MenuItem } from '@/types/menu.js';
import { GridContext } from '@/components/grid/grid-event.js';
export const defaultGridRowSetting: Required<GridRowSetting> = {
showNumber: true,
selectable: true,
minimumDefinitionCount: 100,
styleRules: [],
contextMenuFactory: () => [],
};
export type GridRowStyleRuleConditionParams = {
@ -20,25 +23,30 @@ export type GridRowStyleRule = {
applyStyle: AdditionalStyle;
}
export type GridRowContextMenuFactory = (row: GridRow, context: GridContext) => MenuItem[];
export type GridRowSetting = {
showNumber?: boolean;
selectable?: boolean;
minimumDefinitionCount?: number;
styleRules?: GridRowStyleRule[];
contextMenuFactory?: GridRowContextMenuFactory;
}
export type GridRow = {
index: number;
ranged: boolean;
using: boolean;
setting: GridRowSetting;
additionalStyles: AdditionalStyle[];
}
export function createRow(index: number, using: boolean): GridRow {
export function createRow(index: number, using: boolean, setting: GridRowSetting): GridRow {
return {
index,
ranged: false,
using: using,
setting,
additionalStyles: [],
};
}

View file

@ -137,13 +137,11 @@ import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue';
import { validators } from '@/components/grid/cell-validators.js';
import {
GridCellContextMenuEvent,
GridCellValidationEvent,
GridCellValueChangeEvent,
GridCurrentState,
GridContext,
GridEvent,
GridKeyDownEvent,
GridRowContextMenuEvent,
} from '@/components/grid/grid-event.js';
import { optInGridUtils } from '@/components/grid/optin-utils.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
@ -209,6 +207,26 @@ function setupGrid(): GridSetting {
applyStyle: { className: 'violationRow' },
},
],
contextMenuFactory: (row, context) => {
return [
{
type: 'button',
text: '選択行をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(gridItems, context),
},
{
type: 'button',
text: '選択行を削除対象とする',
icon: 'ti ti-trash',
action: () => {
for (const row of context.rangedRows) {
gridItems.value[row.index].checked = true;
}
},
},
];
},
},
cols: [
{ bindTo: 'checked', icon: 'ti-trash', type: 'boolean', editable: true, width: 34 },
@ -224,6 +242,34 @@ function setupGrid(): GridSetting {
{ bindTo: 'publicUrl', type: 'text', editable: false, width: 180 },
{ bindTo: 'originalUrl', type: 'text', editable: false, width: 180 },
],
cells: {
contextMenuFactory: (col, row, value, context) => {
return [
{
type: 'button',
text: '選択範囲をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(gridItems, context),
},
{
type: 'button',
text: '選択範囲を削除',
icon: 'ti ti-trash',
action: () => optInGridUtils.deleteSelectionRange(gridItems, context),
},
{
type: 'button',
text: '選択行を削除対象とする',
icon: 'ti ti-trash',
action: () => {
for (const rowIdx of [...new Set(context.rangedCells.map(it => it.row.index)).values()]) {
gridItems.value[rowIdx].checked = true;
}
},
},
];
},
},
};
}
@ -407,17 +453,11 @@ async function onPageChanged(pageNumber: number) {
await refreshCustomEmojis();
}
function onGridEvent(event: GridEvent, currentState: GridCurrentState) {
function onGridEvent(event: GridEvent, currentState: GridContext) {
switch (event.type) {
case 'cell-validation':
onGridCellValidation(event);
break;
case 'row-context-menu':
onGridRowContextMenu(event, currentState);
break;
case 'cell-context-menu':
onGridCellContextMenu(event, currentState);
break;
case 'cell-value-change':
onGridCellValueChange(event);
break;
@ -431,54 +471,6 @@ function onGridCellValidation(event: GridCellValidationEvent) {
updateButtonDisabled.value = event.all.filter(it => !it.valid).length > 0;
}
function onGridRowContextMenu(event: GridRowContextMenuEvent, currentState: GridCurrentState) {
event.menuItems.push(
{
type: 'button',
text: '選択行をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(gridItems, currentState),
},
{
type: 'button',
text: '選択行を削除対象とする',
icon: 'ti ti-trash',
action: () => {
for (const row of currentState.rangedRows) {
gridItems.value[row.index].checked = true;
}
},
},
);
}
function onGridCellContextMenu(event: GridCellContextMenuEvent, currentState: GridCurrentState) {
event.menuItems.push(
{
type: 'button',
text: '選択範囲をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(gridItems, currentState),
},
{
type: 'button',
text: '選択範囲を削除',
icon: 'ti ti-trash',
action: () => optInGridUtils.deleteSelectionRange(gridItems, currentState),
},
{
type: 'button',
text: '選択行を削除対象とする',
icon: 'ti ti-trash',
action: () => {
for (const rowIdx of [...new Set(currentState.rangedCells.map(it => it.row.index)).values()]) {
gridItems.value[rowIdx].checked = true;
}
},
},
);
}
function onGridCellValueChange(event: GridCellValueChangeEvent) {
const { row, column, newValue } = event;
if (gridItems.value.length > row.index && column.setting.bindTo in gridItems.value[row.index]) {
@ -492,7 +484,7 @@ function onGridCellValueChange(event: GridCellValueChangeEvent) {
}
}
async function onGridKeyDown(event: GridKeyDownEvent, currentState: GridCurrentState) {
async function onGridKeyDown(event: GridKeyDownEvent, currentState: GridContext) {
const { ctrlKey, shiftKey, code } = event.event;
switch (true) {

View file

@ -26,14 +26,11 @@
<script setup lang="ts">
import { computed, ref, toRefs } from 'vue';
import { GridColumnSetting } from '@/components/grid/column.js';
import { RequestLogItem } from '@/pages/admin/custom-emojis-grid.impl.js';
import {
GridCellContextMenuEvent,
GridCurrentState,
GridContext,
GridEvent,
GridKeyDownEvent,
GridRowContextMenuEvent,
} from '@/components/grid/grid-event.js';
import { optInGridUtils } from '@/components/grid/optin-utils.js';
import MkGrid from '@/components/grid/MkGrid.vue';
@ -45,6 +42,16 @@ function setupGrid(): GridSetting {
row: {
showNumber: false,
selectable: false,
contextMenuFactory: (row, context) => {
return [
{
type: 'button',
text: '選択行をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(logs, context),
},
];
},
},
cols: [
{ bindTo: 'failed', title: 'failed', type: 'boolean', editable: false, width: 50 },
@ -52,6 +59,18 @@ function setupGrid(): GridSetting {
{ bindTo: 'name', title: 'name', type: 'text', editable: false, width: 140 },
{ bindTo: 'error', title: 'log', type: 'text', editable: false, width: 'auto' },
],
cells: {
contextMenuFactory: (col, row, value, context) => {
return [
{
type: 'button',
text: '選択範囲をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(logs, context),
},
];
},
},
};
}
@ -67,55 +86,15 @@ const filteredLogs = computed(() => {
return logs.value.filter((log) => forceShowing || log.failed);
});
function onGridEvent(event: GridEvent, currentState: GridCurrentState) {
function onGridEvent(event: GridEvent, currentState: GridContext) {
switch (event.type) {
case 'row-context-menu':
onGridRowContextMenu(event, currentState);
break;
case 'cell-context-menu':
onGridCellContextMenu(event, currentState);
break;
case 'keydown':
onGridKeyDown(event, currentState);
break;
}
}
function onGridRowContextMenu(event: GridRowContextMenuEvent, currentState: GridCurrentState) {
event.menuItems.push(
{
type: 'button',
text: '選択行をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(logs, currentState),
},
{
type: 'button',
text: '選択行を削除',
icon: 'ti ti-trash',
action: () => optInGridUtils.deleteSelectionRange(logs, currentState),
},
);
}
function onGridCellContextMenu(event: GridCellContextMenuEvent, currentState: GridCurrentState) {
event.menuItems.push(
{
type: 'button',
text: '選択範囲をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(logs, currentState),
},
{
type: 'button',
text: '選択行を削除',
icon: 'ti ti-trash',
action: () => optInGridUtils.deleteSelectionRange(logs, currentState),
},
);
}
function onGridKeyDown(event: GridKeyDownEvent, currentState: GridCurrentState) {
function onGridKeyDown(event: GridKeyDownEvent, currentState: GridContext) {
optInGridUtils.defaultKeyDownHandler(logs, event, currentState);
}

View file

@ -88,13 +88,11 @@ import { validators } from '@/components/grid/cell-validators.js';
import { chooseFileFromDrive, chooseFileFromPc } from '@/scripts/select-file.js';
import { uploadFile } from '@/scripts/upload.js';
import {
GridCellContextMenuEvent,
GridCellValidationEvent,
GridCellValueChangeEvent,
GridCurrentState,
GridContext,
GridEvent,
GridKeyDownEvent,
GridRowContextMenuEvent,
} from '@/components/grid/grid-event.js';
import { DroppedFile, extractDroppedItems, flattenDroppedFiles } from '@/scripts/file-drop.js';
import { optInGridUtils } from '@/components/grid/optin-utils.js';
@ -136,6 +134,22 @@ function setupGrid(): GridSetting {
applyStyle: { className: 'violationRow' },
},
],
contextMenuFactory: (row, context) => {
return [
{
type: 'button',
text: '選択行をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(gridItems, context),
},
{
type: 'button',
text: '選択行を削除',
icon: 'ti ti-trash',
action: () => optInGridUtils.deleteSelectionRange(gridItems, context),
},
];
},
},
cols: [
{ bindTo: 'url', icon: 'ti-icons', type: 'image', editable: false, width: 'auto', validators: [required] },
@ -147,6 +161,24 @@ function setupGrid(): GridSetting {
{ bindTo: 'localOnly', title: 'localOnly', type: 'boolean', editable: true, width: 90 },
{ bindTo: 'roleIdsThatCanBeUsedThisEmojiAsReaction', title: 'role', type: 'text', editable: true, width: 100 },
],
cells: {
contextMenuFactory: (col, row, value, context) => {
return [
{
type: 'button',
text: '選択範囲をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(gridItems, context),
},
{
type: 'button',
text: '選択行を削除',
icon: 'ti ti-trash',
action: () => optInGridUtils.deleteSelectionRange(gridItems, context),
},
];
},
},
};
}
@ -300,17 +332,11 @@ async function onDriveSelectClicked() {
gridItems.value.push(...driveFiles.map(fromDriveFile));
}
function onGridEvent(event: GridEvent, currentState: GridCurrentState) {
function onGridEvent(event: GridEvent, currentState: GridContext) {
switch (event.type) {
case 'cell-validation':
onGridCellValidation(event);
break;
case 'row-context-menu':
onGridRowContextMenu(event, currentState);
break;
case 'cell-context-menu':
onGridCellContextMenu(event, currentState);
break;
case 'cell-value-change':
onGridCellValueChange(event);
break;
@ -324,40 +350,6 @@ function onGridCellValidation(event: GridCellValidationEvent) {
registerButtonDisabled.value = event.all.filter(it => !it.valid).length > 0;
}
function onGridRowContextMenu(event: GridRowContextMenuEvent, currentState: GridCurrentState) {
event.menuItems.push(
{
type: 'button',
text: '選択行をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(gridItems, currentState),
},
{
type: 'button',
text: '選択行を削除',
icon: 'ti ti-trash',
action: () => optInGridUtils.deleteSelectionRange(gridItems, currentState),
},
);
}
function onGridCellContextMenu(event: GridCellContextMenuEvent, currentState: GridCurrentState) {
event.menuItems.push(
{
type: 'button',
text: '選択範囲をコピー',
icon: 'ti ti-copy',
action: () => optInGridUtils.copyToClipboard(gridItems, currentState),
},
{
type: 'button',
text: '選択行を削除',
icon: 'ti ti-trash',
action: () => optInGridUtils.deleteSelectionRange(gridItems, currentState),
},
);
}
function onGridCellValueChange(event: GridCellValueChangeEvent) {
const { row, column, newValue } = event;
if (gridItems.value.length > row.index && column.setting.bindTo in gridItems.value[row.index]) {
@ -365,7 +357,7 @@ function onGridCellValueChange(event: GridCellValueChangeEvent) {
}
}
function onGridKeyDown(event: GridKeyDownEvent, currentState: GridCurrentState) {
function onGridKeyDown(event: GridKeyDownEvent, currentState: GridContext) {
optInGridUtils.defaultKeyDownHandler(gridItems, event, currentState);
}

View file

@ -51,16 +51,8 @@ import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkGrid from '@/components/grid/MkGrid.vue';
import { GridColumnSetting } from '@/components/grid/column.js';
import { RequestLogItem } from '@/pages/admin/custom-emojis-grid.impl.js';
import {
GridCellContextMenuEvent,
GridCellValueChangeEvent,
GridCurrentState,
GridEvent,
GridKeyDownEvent,
GridRowContextMenuEvent,
} from '@/components/grid/grid-event.js';
import { GridCellValueChangeEvent, GridContext, GridEvent, GridKeyDownEvent } from '@/components/grid/grid-event.js';
import { optInGridUtils } from '@/components/grid/optin-utils.js';
import MkFolder from '@/components/MkFolder.vue';
import XRegisterLogs from '@/pages/admin/custom-emojis-grid.local.logs.vue';
@ -77,12 +69,42 @@ type GridItem = {
function setupGrid(): GridSetting {
return {
row: {
contextMenuFactory: (row, context) => {
return [
{
type: 'button',
text: '選択行をインポート',
icon: 'ti ti-download',
action: async () => {
const targets = context.rangedRows.map(it => gridItems.value[it.index]);
await importEmojis(targets);
},
},
];
},
},
cols: [
{ bindTo: 'checked', icon: 'ti-download', type: 'boolean', editable: true, width: 34 },
{ bindTo: 'url', icon: 'ti-icons', type: 'image', editable: false, width: 'auto' },
{ bindTo: 'name', title: 'name', type: 'text', editable: false, width: 'auto' },
{ bindTo: 'host', title: 'host', type: 'text', editable: false, width: 'auto' },
],
cells: {
contextMenuFactory: (col, row, value, context) => {
return [
{
type: 'button',
text: '選択範囲の行をインポート',
icon: 'ti ti-download',
action: async () => {
const targets = context.rangedCells.map(it => gridItems.value[it.row.index]);
await importEmojis(targets);
},
},
];
},
},
};
}
@ -113,14 +135,8 @@ async function onImportClicked() {
await importEmojis(targets);
}
function onGridEvent(event: GridEvent, currentState: GridCurrentState) {
function onGridEvent(event: GridEvent, currentState: GridContext) {
switch (event.type) {
case 'row-context-menu':
onGridRowContextMenu(event, currentState);
break;
case 'cell-context-menu':
onGridCellContextMenu(event, currentState);
break;
case 'cell-value-change':
onGridCellValueChange(event);
break;
@ -130,35 +146,6 @@ function onGridEvent(event: GridEvent, currentState: GridCurrentState) {
}
}
function onGridRowContextMenu(event: GridRowContextMenuEvent, currentState: GridCurrentState) {
event.menuItems.push(
{
type: 'button',
text: '選択行をインポート',
icon: 'ti ti-download',
action: async () => {
const targets = currentState.rangedRows.map(it => gridItems.value[it.index]);
console.log(targets);
await importEmojis(targets);
},
},
);
}
function onGridCellContextMenu(event: GridCellContextMenuEvent, currentState: GridCurrentState) {
event.menuItems.push(
{
type: 'button',
text: '選択された絵文字をインポート',
icon: 'ti ti-download',
action: async () => {
const targets = [...new Set(currentState.rangedCells.map(it => it.row)).values()].map(it => gridItems.value[it.index]);
await importEmojis(targets);
},
},
);
}
function onGridCellValueChange(event: GridCellValueChangeEvent) {
const { row, column, newValue } = event;
if (gridItems.value.length > row.index && column.setting.bindTo in gridItems.value[row.index]) {
@ -166,7 +153,7 @@ function onGridCellValueChange(event: GridCellValueChangeEvent) {
}
}
function onGridKeyDown(event: GridKeyDownEvent, currentState: GridCurrentState) {
function onGridKeyDown(event: GridKeyDownEvent, currentState: GridContext) {
optInGridUtils.defaultKeyDownHandler(gridItems, event, currentState);
}