mirror of
https://github.com/misskey-dev/misskey.git
synced 2025-01-06 01:46:10 +01:00
refine context menu setting
This commit is contained in:
parent
369d5971d4
commit
c0f941689b
11 changed files with 243 additions and 255 deletions
|
@ -48,7 +48,7 @@ import { CELL_ADDRESS_NONE, CellAddress, CellValue, createCell, GridCell, resetC
|
||||||
import { equalCellAddress, getCellAddress, getCellElement } from '@/components/grid/grid-utils.js';
|
import { equalCellAddress, getCellAddress, getCellElement } from '@/components/grid/grid-utils.js';
|
||||||
import { MenuItem } from '@/types/menu.js';
|
import { MenuItem } from '@/types/menu.js';
|
||||||
import * as os from '@/os.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 { createColumn, GridColumn } from '@/components/grid/column.js';
|
||||||
import { createRow, defaultGridRowSetting, GridRow, GridRowSetting, resetRow } from '@/components/grid/row.js';
|
import { createRow, defaultGridRowSetting, GridRow, GridRowSetting, resetRow } from '@/components/grid/row.js';
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ type RowHolder = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'event', event: GridEvent, current: GridCurrentState): void;
|
(ev: 'event', event: GridEvent, current: GridContext): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -76,6 +76,9 @@ const rowSetting: Required<GridRowSetting> = {
|
||||||
// non-reactive
|
// non-reactive
|
||||||
const columnSettings = props.settings.cols;
|
const columnSettings = props.settings.cols;
|
||||||
|
|
||||||
|
// non-reactive
|
||||||
|
const cellSettings = props.settings.cells ?? {};
|
||||||
|
|
||||||
const { data } = toRefs(props);
|
const { data } = toRefs(props);
|
||||||
|
|
||||||
// #region Event Definitions
|
// #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}`);
|
console.log(`[grid][context-menu] button: ${ev.button}, cell: ${cellAddress.row}x${cellAddress.col}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const context = createContext();
|
||||||
const menuItems = Array.of<MenuItem>();
|
const menuItems = Array.of<MenuItem>();
|
||||||
|
switch (true) {
|
||||||
// 外でメニュー項目を挿してもらう
|
case availableCellAddress(cellAddress): {
|
||||||
if (availableCellAddress(cellAddress)) {
|
const cell = cells.value[cellAddress.row].cells[cellAddress.col];
|
||||||
emitGridEvent({ type: 'cell-context-menu', event: ev, menuItems });
|
if (cell.setting.contextMenuFactory) {
|
||||||
} else if (isRowNumberCellAddress(cellAddress)) {
|
menuItems.push(...cell.setting.contextMenuFactory(cell.column, cell.row, cell.value, context));
|
||||||
emitGridEvent({ type: 'row-context-menu', event: ev, menuItems });
|
}
|
||||||
} else if (isColumnHeaderCellAddress(cellAddress)) {
|
break;
|
||||||
emitGridEvent({ type: 'column-context-menu', event: ev, menuItems });
|
}
|
||||||
|
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) {
|
if (menuItems.length > 0) {
|
||||||
|
@ -835,7 +853,7 @@ function calcLargestCellWidth(column: GridColumn) {
|
||||||
* {@link emit}を使用してイベントを発行する。
|
* {@link emit}を使用してイベントを発行する。
|
||||||
*/
|
*/
|
||||||
function emitGridEvent(ev: GridEvent) {
|
function emitGridEvent(ev: GridEvent) {
|
||||||
const currentState: GridCurrentState = {
|
const currentState: GridContext = {
|
||||||
selectedCell: selectedCell.value,
|
selectedCell: selectedCell.value,
|
||||||
rangedCells: rangedCells.value,
|
rangedCells: rangedCells.value,
|
||||||
rangedRows: rangedRows.value,
|
rangedRows: rangedRows.value,
|
||||||
|
@ -1037,6 +1055,19 @@ function unregisterMouseUp() {
|
||||||
removeEventListener('mouseup', onMouseUp);
|
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() {
|
function refreshData() {
|
||||||
if (_DEV_) {
|
if (_DEV_) {
|
||||||
console.log('[grid][refresh-data][begin]');
|
console.log('[grid][refresh-data][begin]');
|
||||||
|
@ -1044,8 +1075,8 @@ function refreshData() {
|
||||||
|
|
||||||
const _data: DataSource[] = data.value;
|
const _data: DataSource[] = data.value;
|
||||||
const _rows: GridRow[] = (_data.length > rowSetting.minimumDefinitionCount)
|
const _rows: GridRow[] = (_data.length > rowSetting.minimumDefinitionCount)
|
||||||
? _data.map((_, index) => createRow(index, true))
|
? _data.map((_, index) => createRow(index, true, rowSetting))
|
||||||
: Array.from({ length: rowSetting.minimumDefinitionCount }, (_, index) => createRow(index, index < _data.length));
|
: Array.from({ length: rowSetting.minimumDefinitionCount }, (_, index) => createRow(index, index < _data.length, rowSetting));
|
||||||
const _cols: GridColumn[] = columns.value;
|
const _cols: GridColumn[] = columns.value;
|
||||||
|
|
||||||
// 行・列の定義から、元データの配列より値を取得してセルを作成する。
|
// 行・列の定義から、元データの配列より値を取得してセルを作成する。
|
||||||
|
@ -1053,11 +1084,11 @@ function refreshData() {
|
||||||
const _cells: RowHolder[] = _rows.map(row => {
|
const _cells: RowHolder[] = _rows.map(row => {
|
||||||
const cells = row.using
|
const cells = row.using
|
||||||
? _cols.map(col => {
|
? _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);
|
cell.violation = cellValidation(cell, cell.value);
|
||||||
return cell;
|
return cell;
|
||||||
})
|
})
|
||||||
: _cols.map(col => createCell(col, row, undefined));
|
: _cols.map(col => createCell(col, row, undefined, cellSettings));
|
||||||
|
|
||||||
return { row, cells, origin: _data[row.index] };
|
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++) {
|
for (let rowIdx = rows.value.length; rowIdx < newItems.length; rowIdx++) {
|
||||||
const newRow = createRow(rowIdx, true);
|
const newRow = createRow(rowIdx, true, rowSetting);
|
||||||
newRows.push(newRow);
|
newRows.push(newRow);
|
||||||
newCells.push({
|
newCells.push({
|
||||||
row: newRow,
|
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],
|
origin: newItems[rowIdx],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { ValidateViolation } from '@/components/grid/cell-validators.js';
|
||||||
import { Size } from '@/components/grid/grid.js';
|
import { Size } from '@/components/grid/grid.js';
|
||||||
import { GridColumn } from '@/components/grid/column.js';
|
import { GridColumn } from '@/components/grid/column.js';
|
||||||
import { GridRow } from '@/components/grid/row.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
|
export type CellValue = string | boolean | number | undefined | null
|
||||||
|
|
||||||
|
@ -23,13 +25,21 @@ export type GridCell = {
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
ranged: boolean;
|
ranged: boolean;
|
||||||
contentSize: Size;
|
contentSize: Size;
|
||||||
|
setting: GridCellSetting;
|
||||||
violation: ValidateViolation;
|
violation: ValidateViolation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GridCellContextMenuFactory = (col: GridColumn, row: GridRow, value: CellValue, context: GridContext) => MenuItem[];
|
||||||
|
|
||||||
|
export type GridCellSetting = {
|
||||||
|
contextMenuFactory?: GridCellContextMenuFactory;
|
||||||
|
}
|
||||||
|
|
||||||
export function createCell(
|
export function createCell(
|
||||||
column: GridColumn,
|
column: GridColumn,
|
||||||
row: GridRow,
|
row: GridRow,
|
||||||
value: CellValue,
|
value: CellValue,
|
||||||
|
setting: GridCellSetting,
|
||||||
): GridCell {
|
): GridCell {
|
||||||
return {
|
return {
|
||||||
address: { row: row.index, col: column.index },
|
address: { row: row.index, col: column.index },
|
||||||
|
@ -48,6 +58,7 @@ export function createCell(
|
||||||
},
|
},
|
||||||
violations: [],
|
violations: [],
|
||||||
},
|
},
|
||||||
|
setting,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
import { CellValidator } from '@/components/grid/cell-validators.js';
|
import { CellValidator } from '@/components/grid/cell-validators.js';
|
||||||
import { Size, SizeStyle } from '@/components/grid/grid.js';
|
import { Size, SizeStyle } from '@/components/grid/grid.js';
|
||||||
import { calcCellWidth } from '@/components/grid/grid-utils.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 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 = {
|
export type GridColumnSetting = {
|
||||||
bindTo: string;
|
bindTo: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -12,6 +19,8 @@ export type GridColumnSetting = {
|
||||||
width: SizeStyle;
|
width: SizeStyle;
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
validators?: CellValidator[];
|
validators?: CellValidator[];
|
||||||
|
valueConverter?: CellValueConverter;
|
||||||
|
contextMenuFactory?: GridColumnContextMenuFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GridColumn = {
|
export type GridColumn = {
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { CellAddress, CellValue, GridCell } from '@/components/grid/cell.js';
|
import { CellAddress, CellValue, GridCell } from '@/components/grid/cell.js';
|
||||||
import { GridState } from '@/components/grid/grid.js';
|
import { GridState } from '@/components/grid/grid.js';
|
||||||
import { ValidateViolation } from '@/components/grid/cell-validators.js';
|
import { ValidateViolation } from '@/components/grid/cell-validators.js';
|
||||||
import { MenuItem } from '@/types/menu.js';
|
|
||||||
import { GridColumn } from '@/components/grid/column.js';
|
import { GridColumn } from '@/components/grid/column.js';
|
||||||
import { GridRow } from '@/components/grid/row.js';
|
import { GridRow } from '@/components/grid/row.js';
|
||||||
|
|
||||||
export type GridCurrentState = {
|
export type GridContext = {
|
||||||
selectedCell?: GridCell;
|
selectedCell?: GridCell;
|
||||||
rangedCells: GridCell[];
|
rangedCells: GridCell[];
|
||||||
rangedRows: GridRow[];
|
rangedRows: GridRow[];
|
||||||
|
@ -26,10 +25,7 @@ export type GridEvent =
|
||||||
GridCellValueChangeEvent |
|
GridCellValueChangeEvent |
|
||||||
GridKeyDownEvent |
|
GridKeyDownEvent |
|
||||||
GridMouseDownEvent |
|
GridMouseDownEvent |
|
||||||
GridCellValidationEvent |
|
GridCellValidationEvent
|
||||||
GridCellContextMenuEvent |
|
|
||||||
GridRowContextMenuEvent |
|
|
||||||
GridColumnContextMenuEvent
|
|
||||||
;
|
;
|
||||||
|
|
||||||
export type GridCellValueChangeEvent = {
|
export type GridCellValueChangeEvent = {
|
||||||
|
@ -57,21 +53,3 @@ export type GridMouseDownEvent = {
|
||||||
event: MouseEvent;
|
event: MouseEvent;
|
||||||
clickedCellAddress: CellAddress;
|
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[];
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { EventEmitter } from 'eventemitter3';
|
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 { GridColumnSetting } from '@/components/grid/column.js';
|
||||||
import { GridRowSetting } from '@/components/grid/row.js';
|
import { GridRowSetting } from '@/components/grid/row.js';
|
||||||
|
|
||||||
export type GridSetting = {
|
export type GridSetting = {
|
||||||
row?: GridRowSetting;
|
row?: GridRowSetting;
|
||||||
cols: GridColumnSetting[];
|
cols: GridColumnSetting[];
|
||||||
|
cells?: GridCellSetting;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DataSource = Record<string, CellValue>;
|
export type DataSource = Record<string, CellValue>;
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { Ref } from 'vue';
|
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 copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||||
import { GridColumnSetting } from '@/components/grid/column.js';
|
import { GridColumnSetting } from '@/components/grid/column.js';
|
||||||
import { CellValue } from '@/components/grid/cell.js';
|
import { CellValue } from '@/components/grid/cell.js';
|
||||||
import { DataSource } from '@/components/grid/grid.js';
|
import { DataSource } from '@/components/grid/grid.js';
|
||||||
|
|
||||||
class OptInGridUtils {
|
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;
|
const { ctrlKey, shiftKey, code } = event.event;
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
|
@ -16,11 +16,11 @@ class OptInGridUtils {
|
||||||
case ctrlKey: {
|
case ctrlKey: {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'KeyC': {
|
case 'KeyC': {
|
||||||
this.copyToClipboard(gridItems, currentState);
|
this.copyToClipboard(gridItems, context);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'KeyV': {
|
case 'KeyV': {
|
||||||
await this.pasteFromClipboard(gridItems, currentState);
|
await this.pasteFromClipboard(gridItems, context);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ class OptInGridUtils {
|
||||||
default: {
|
default: {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'Delete': {
|
case 'Delete': {
|
||||||
this.deleteSelectionRange(gridItems, currentState);
|
this.deleteSelectionRange(gridItems, context);
|
||||||
break;
|
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 lines = Array.of<string>();
|
||||||
const bounds = currentState.randedBounds;
|
const bounds = context.randedBounds;
|
||||||
|
|
||||||
for (let row = bounds.leftTop.row; row <= bounds.rightBottom.row; row++) {
|
for (let row = bounds.leftTop.row; row <= bounds.rightBottom.row; row++) {
|
||||||
const items = Array.of<string>();
|
const items = Array.of<string>();
|
||||||
for (let col = bounds.leftTop.col; col <= bounds.rightBottom.col; col++) {
|
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];
|
const cell = gridItems.value[row][bindTo];
|
||||||
items.push(cell?.toString() ?? '');
|
items.push(cell?.toString() ?? '');
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ class OptInGridUtils {
|
||||||
|
|
||||||
async pasteFromClipboard(
|
async pasteFromClipboard(
|
||||||
gridItems: Ref<DataSource[]>,
|
gridItems: Ref<DataSource[]>,
|
||||||
currentState: GridCurrentState,
|
context: GridContext,
|
||||||
) {
|
) {
|
||||||
function parseValue(value: string, type: GridColumnSetting['type']): CellValue {
|
function parseValue(value: string, type: GridColumnSetting['type']): CellValue {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -86,14 +86,14 @@ class OptInGridUtils {
|
||||||
console.log(`Paste from clipboard: ${clipBoardText}`);
|
console.log(`Paste from clipboard: ${clipBoardText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bounds = currentState.randedBounds;
|
const bounds = context.randedBounds;
|
||||||
const lines = clipBoardText.replace(/\r/g, '')
|
const lines = clipBoardText.replace(/\r/g, '')
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map(it => it.split('\t'));
|
.map(it => it.split('\t'));
|
||||||
|
|
||||||
if (lines.length === 1 && lines[0].length === 1) {
|
if (lines.length === 1 && lines[0].length === 1) {
|
||||||
// 単独文字列の場合は選択範囲全体に同じテキストを貼り付ける
|
// 単独文字列の場合は選択範囲全体に同じテキストを貼り付ける
|
||||||
const ranges = currentState.rangedCells;
|
const ranges = context.rangedCells;
|
||||||
for (const cell of ranges) {
|
for (const cell of ranges) {
|
||||||
gridItems.value[cell.row.index][cell.column.setting.bindTo] = parseValue(lines[0][0], cell.column.setting.type);
|
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 offsetRow = bounds.leftTop.row;
|
||||||
const offsetCol = bounds.leftTop.col;
|
const offsetCol = bounds.leftTop.col;
|
||||||
const columns = currentState.columns;
|
const columns = context.columns;
|
||||||
for (let row = bounds.leftTop.row; row <= bounds.rightBottom.row; row++) {
|
for (let row = bounds.leftTop.row; row <= bounds.rightBottom.row; row++) {
|
||||||
const rowIdx = row - offsetRow;
|
const rowIdx = row - offsetRow;
|
||||||
if (lines.length <= rowIdx) {
|
if (lines.length <= rowIdx) {
|
||||||
|
@ -123,12 +123,12 @@ class OptInGridUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteSelectionRange(gridItems: Ref<DataSource[]>, currentState: GridCurrentState) {
|
deleteSelectionRange(gridItems: Ref<DataSource[]>, context: GridContext) {
|
||||||
if (currentState.rangedRows.length > 0) {
|
if (context.rangedRows.length > 0) {
|
||||||
const deletedIndexes = currentState.rangedRows.map(it => it.index);
|
const deletedIndexes = context.rangedRows.map(it => it.index);
|
||||||
gridItems.value = gridItems.value.filter((_, index) => !deletedIndexes.includes(index));
|
gridItems.value = gridItems.value.filter((_, index) => !deletedIndexes.includes(index));
|
||||||
} else {
|
} else {
|
||||||
const ranges = currentState.rangedCells;
|
const ranges = context.rangedCells;
|
||||||
for (const cell of ranges) {
|
for (const cell of ranges) {
|
||||||
if (cell.column.setting.editable) {
|
if (cell.column.setting.editable) {
|
||||||
gridItems.value[cell.row.index][cell.column.setting.bindTo] = undefined;
|
gridItems.value[cell.row.index][cell.column.setting.bindTo] = undefined;
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import { AdditionalStyle } from '@/components/grid/grid.js';
|
import { AdditionalStyle } from '@/components/grid/grid.js';
|
||||||
import { GridCell } from '@/components/grid/cell.js';
|
import { GridCell } from '@/components/grid/cell.js';
|
||||||
import { GridColumn } from '@/components/grid/column.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> = {
|
export const defaultGridRowSetting: Required<GridRowSetting> = {
|
||||||
showNumber: true,
|
showNumber: true,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
minimumDefinitionCount: 100,
|
minimumDefinitionCount: 100,
|
||||||
styleRules: [],
|
styleRules: [],
|
||||||
|
contextMenuFactory: () => [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GridRowStyleRuleConditionParams = {
|
export type GridRowStyleRuleConditionParams = {
|
||||||
|
@ -20,25 +23,30 @@ export type GridRowStyleRule = {
|
||||||
applyStyle: AdditionalStyle;
|
applyStyle: AdditionalStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GridRowContextMenuFactory = (row: GridRow, context: GridContext) => MenuItem[];
|
||||||
|
|
||||||
export type GridRowSetting = {
|
export type GridRowSetting = {
|
||||||
showNumber?: boolean;
|
showNumber?: boolean;
|
||||||
selectable?: boolean;
|
selectable?: boolean;
|
||||||
minimumDefinitionCount?: number;
|
minimumDefinitionCount?: number;
|
||||||
styleRules?: GridRowStyleRule[];
|
styleRules?: GridRowStyleRule[];
|
||||||
|
contextMenuFactory?: GridRowContextMenuFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GridRow = {
|
export type GridRow = {
|
||||||
index: number;
|
index: number;
|
||||||
ranged: boolean;
|
ranged: boolean;
|
||||||
using: boolean;
|
using: boolean;
|
||||||
|
setting: GridRowSetting;
|
||||||
additionalStyles: AdditionalStyle[];
|
additionalStyles: AdditionalStyle[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createRow(index: number, using: boolean): GridRow {
|
export function createRow(index: number, using: boolean, setting: GridRowSetting): GridRow {
|
||||||
return {
|
return {
|
||||||
index,
|
index,
|
||||||
ranged: false,
|
ranged: false,
|
||||||
using: using,
|
using: using,
|
||||||
|
setting,
|
||||||
additionalStyles: [],
|
additionalStyles: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,13 +137,11 @@ import MkInput from '@/components/MkInput.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { validators } from '@/components/grid/cell-validators.js';
|
import { validators } from '@/components/grid/cell-validators.js';
|
||||||
import {
|
import {
|
||||||
GridCellContextMenuEvent,
|
|
||||||
GridCellValidationEvent,
|
GridCellValidationEvent,
|
||||||
GridCellValueChangeEvent,
|
GridCellValueChangeEvent,
|
||||||
GridCurrentState,
|
GridContext,
|
||||||
GridEvent,
|
GridEvent,
|
||||||
GridKeyDownEvent,
|
GridKeyDownEvent,
|
||||||
GridRowContextMenuEvent,
|
|
||||||
} from '@/components/grid/grid-event.js';
|
} from '@/components/grid/grid-event.js';
|
||||||
import { optInGridUtils } from '@/components/grid/optin-utils.js';
|
import { optInGridUtils } from '@/components/grid/optin-utils.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
|
@ -209,6 +207,26 @@ function setupGrid(): GridSetting {
|
||||||
applyStyle: { className: 'violationRow' },
|
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: [
|
cols: [
|
||||||
{ bindTo: 'checked', icon: 'ti-trash', type: 'boolean', editable: true, width: 34 },
|
{ 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: 'publicUrl', type: 'text', editable: false, width: 180 },
|
||||||
{ bindTo: 'originalUrl', 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();
|
await refreshCustomEmojis();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onGridEvent(event: GridEvent, currentState: GridCurrentState) {
|
function onGridEvent(event: GridEvent, currentState: GridContext) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case 'cell-validation':
|
case 'cell-validation':
|
||||||
onGridCellValidation(event);
|
onGridCellValidation(event);
|
||||||
break;
|
break;
|
||||||
case 'row-context-menu':
|
|
||||||
onGridRowContextMenu(event, currentState);
|
|
||||||
break;
|
|
||||||
case 'cell-context-menu':
|
|
||||||
onGridCellContextMenu(event, currentState);
|
|
||||||
break;
|
|
||||||
case 'cell-value-change':
|
case 'cell-value-change':
|
||||||
onGridCellValueChange(event);
|
onGridCellValueChange(event);
|
||||||
break;
|
break;
|
||||||
|
@ -431,54 +471,6 @@ function onGridCellValidation(event: GridCellValidationEvent) {
|
||||||
updateButtonDisabled.value = event.all.filter(it => !it.valid).length > 0;
|
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) {
|
function onGridCellValueChange(event: GridCellValueChangeEvent) {
|
||||||
const { row, column, newValue } = event;
|
const { row, column, newValue } = event;
|
||||||
if (gridItems.value.length > row.index && column.setting.bindTo in gridItems.value[row.index]) {
|
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;
|
const { ctrlKey, shiftKey, code } = event.event;
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
|
|
|
@ -26,14 +26,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import { computed, ref, toRefs } from 'vue';
|
import { computed, ref, toRefs } from 'vue';
|
||||||
import { GridColumnSetting } from '@/components/grid/column.js';
|
|
||||||
import { RequestLogItem } from '@/pages/admin/custom-emojis-grid.impl.js';
|
import { RequestLogItem } from '@/pages/admin/custom-emojis-grid.impl.js';
|
||||||
import {
|
import {
|
||||||
GridCellContextMenuEvent,
|
GridContext,
|
||||||
GridCurrentState,
|
|
||||||
GridEvent,
|
GridEvent,
|
||||||
GridKeyDownEvent,
|
GridKeyDownEvent,
|
||||||
GridRowContextMenuEvent,
|
|
||||||
} from '@/components/grid/grid-event.js';
|
} from '@/components/grid/grid-event.js';
|
||||||
import { optInGridUtils } from '@/components/grid/optin-utils.js';
|
import { optInGridUtils } from '@/components/grid/optin-utils.js';
|
||||||
import MkGrid from '@/components/grid/MkGrid.vue';
|
import MkGrid from '@/components/grid/MkGrid.vue';
|
||||||
|
@ -45,6 +42,16 @@ function setupGrid(): GridSetting {
|
||||||
row: {
|
row: {
|
||||||
showNumber: false,
|
showNumber: false,
|
||||||
selectable: false,
|
selectable: false,
|
||||||
|
contextMenuFactory: (row, context) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: 'button',
|
||||||
|
text: '選択行をコピー',
|
||||||
|
icon: 'ti ti-copy',
|
||||||
|
action: () => optInGridUtils.copyToClipboard(logs, context),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cols: [
|
cols: [
|
||||||
{ bindTo: 'failed', title: 'failed', type: 'boolean', editable: false, width: 50 },
|
{ 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: 'name', title: 'name', type: 'text', editable: false, width: 140 },
|
||||||
{ bindTo: 'error', title: 'log', type: 'text', editable: false, width: 'auto' },
|
{ 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);
|
return logs.value.filter((log) => forceShowing || log.failed);
|
||||||
});
|
});
|
||||||
|
|
||||||
function onGridEvent(event: GridEvent, currentState: GridCurrentState) {
|
function onGridEvent(event: GridEvent, currentState: GridContext) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case 'row-context-menu':
|
|
||||||
onGridRowContextMenu(event, currentState);
|
|
||||||
break;
|
|
||||||
case 'cell-context-menu':
|
|
||||||
onGridCellContextMenu(event, currentState);
|
|
||||||
break;
|
|
||||||
case 'keydown':
|
case 'keydown':
|
||||||
onGridKeyDown(event, currentState);
|
onGridKeyDown(event, currentState);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onGridRowContextMenu(event: GridRowContextMenuEvent, currentState: GridCurrentState) {
|
function onGridKeyDown(event: GridKeyDownEvent, currentState: GridContext) {
|
||||||
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) {
|
|
||||||
optInGridUtils.defaultKeyDownHandler(logs, event, currentState);
|
optInGridUtils.defaultKeyDownHandler(logs, event, currentState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,13 +88,11 @@ import { validators } from '@/components/grid/cell-validators.js';
|
||||||
import { chooseFileFromDrive, chooseFileFromPc } from '@/scripts/select-file.js';
|
import { chooseFileFromDrive, chooseFileFromPc } from '@/scripts/select-file.js';
|
||||||
import { uploadFile } from '@/scripts/upload.js';
|
import { uploadFile } from '@/scripts/upload.js';
|
||||||
import {
|
import {
|
||||||
GridCellContextMenuEvent,
|
|
||||||
GridCellValidationEvent,
|
GridCellValidationEvent,
|
||||||
GridCellValueChangeEvent,
|
GridCellValueChangeEvent,
|
||||||
GridCurrentState,
|
GridContext,
|
||||||
GridEvent,
|
GridEvent,
|
||||||
GridKeyDownEvent,
|
GridKeyDownEvent,
|
||||||
GridRowContextMenuEvent,
|
|
||||||
} from '@/components/grid/grid-event.js';
|
} 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 { optInGridUtils } from '@/components/grid/optin-utils.js';
|
import { optInGridUtils } from '@/components/grid/optin-utils.js';
|
||||||
|
@ -136,6 +134,22 @@ function setupGrid(): GridSetting {
|
||||||
applyStyle: { className: 'violationRow' },
|
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: [
|
cols: [
|
||||||
{ bindTo: 'url', icon: 'ti-icons', type: 'image', editable: false, width: 'auto', validators: [required] },
|
{ 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: 'localOnly', title: 'localOnly', type: 'boolean', editable: true, width: 90 },
|
||||||
{ bindTo: 'roleIdsThatCanBeUsedThisEmojiAsReaction', title: 'role', type: 'text', editable: true, width: 100 },
|
{ 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));
|
gridItems.value.push(...driveFiles.map(fromDriveFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
function onGridEvent(event: GridEvent, currentState: GridCurrentState) {
|
function onGridEvent(event: GridEvent, currentState: GridContext) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case 'cell-validation':
|
case 'cell-validation':
|
||||||
onGridCellValidation(event);
|
onGridCellValidation(event);
|
||||||
break;
|
break;
|
||||||
case 'row-context-menu':
|
|
||||||
onGridRowContextMenu(event, currentState);
|
|
||||||
break;
|
|
||||||
case 'cell-context-menu':
|
|
||||||
onGridCellContextMenu(event, currentState);
|
|
||||||
break;
|
|
||||||
case 'cell-value-change':
|
case 'cell-value-change':
|
||||||
onGridCellValueChange(event);
|
onGridCellValueChange(event);
|
||||||
break;
|
break;
|
||||||
|
@ -324,40 +350,6 @@ function onGridCellValidation(event: GridCellValidationEvent) {
|
||||||
registerButtonDisabled.value = event.all.filter(it => !it.valid).length > 0;
|
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) {
|
function onGridCellValueChange(event: GridCellValueChangeEvent) {
|
||||||
const { row, column, newValue } = event;
|
const { row, column, newValue } = event;
|
||||||
if (gridItems.value.length > row.index && column.setting.bindTo in gridItems.value[row.index]) {
|
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);
|
optInGridUtils.defaultKeyDownHandler(gridItems, event, currentState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,16 +51,8 @@ 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 { GridColumnSetting } from '@/components/grid/column.js';
|
|
||||||
import { RequestLogItem } from '@/pages/admin/custom-emojis-grid.impl.js';
|
import { RequestLogItem } from '@/pages/admin/custom-emojis-grid.impl.js';
|
||||||
import {
|
import { GridCellValueChangeEvent, GridContext, GridEvent, GridKeyDownEvent } from '@/components/grid/grid-event.js';
|
||||||
GridCellContextMenuEvent,
|
|
||||||
GridCellValueChangeEvent,
|
|
||||||
GridCurrentState,
|
|
||||||
GridEvent,
|
|
||||||
GridKeyDownEvent,
|
|
||||||
GridRowContextMenuEvent,
|
|
||||||
} from '@/components/grid/grid-event.js';
|
|
||||||
import { optInGridUtils } from '@/components/grid/optin-utils.js';
|
import { optInGridUtils } from '@/components/grid/optin-utils.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import XRegisterLogs from '@/pages/admin/custom-emojis-grid.local.logs.vue';
|
import XRegisterLogs from '@/pages/admin/custom-emojis-grid.local.logs.vue';
|
||||||
|
@ -77,12 +69,42 @@ type GridItem = {
|
||||||
|
|
||||||
function setupGrid(): GridSetting {
|
function setupGrid(): GridSetting {
|
||||||
return {
|
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: [
|
cols: [
|
||||||
{ bindTo: 'checked', icon: 'ti-download', type: 'boolean', editable: true, width: 34 },
|
{ bindTo: 'checked', icon: 'ti-download', type: 'boolean', editable: true, width: 34 },
|
||||||
{ bindTo: 'url', icon: 'ti-icons', type: 'image', editable: false, width: 'auto' },
|
{ bindTo: 'url', icon: 'ti-icons', type: 'image', editable: false, width: 'auto' },
|
||||||
{ bindTo: 'name', title: 'name', type: 'text', editable: false, width: 'auto' },
|
{ bindTo: 'name', title: 'name', type: 'text', editable: false, width: 'auto' },
|
||||||
{ bindTo: 'host', title: 'host', 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);
|
await importEmojis(targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onGridEvent(event: GridEvent, currentState: GridCurrentState) {
|
function onGridEvent(event: GridEvent, currentState: GridContext) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case 'row-context-menu':
|
|
||||||
onGridRowContextMenu(event, currentState);
|
|
||||||
break;
|
|
||||||
case 'cell-context-menu':
|
|
||||||
onGridCellContextMenu(event, currentState);
|
|
||||||
break;
|
|
||||||
case 'cell-value-change':
|
case 'cell-value-change':
|
||||||
onGridCellValueChange(event);
|
onGridCellValueChange(event);
|
||||||
break;
|
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) {
|
function onGridCellValueChange(event: GridCellValueChangeEvent) {
|
||||||
const { row, column, newValue } = event;
|
const { row, column, newValue } = event;
|
||||||
if (gridItems.value.length > row.index && column.setting.bindTo in gridItems.value[row.index]) {
|
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);
|
optInGridUtils.defaultKeyDownHandler(gridItems, event, currentState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue