fix
This commit is contained in:
parent
aacee3c970
commit
e21c43e2aa
|
@ -0,0 +1,35 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<MkTooltip ref="tooltip" :showing="showing" :targetElement="targetElement" :maxWidth="250" @closed="emit('closed')">
|
||||||
|
<div :class="$style.root">
|
||||||
|
{{ content }}
|
||||||
|
</div>
|
||||||
|
</MkTooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { } from 'vue';
|
||||||
|
import MkTooltip from '@/components/MkTooltip.vue';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
showing: boolean;
|
||||||
|
content: string;
|
||||||
|
targetElement: HTMLElement;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(ev: 'closed'): void;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.root {
|
||||||
|
font-size: 0.9em;
|
||||||
|
text-align: left;
|
||||||
|
text-wrap: normal;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -10,9 +10,10 @@
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
$style.root,
|
$style.root,
|
||||||
|
[(cell.validation.valid || cell.selected) ? {} : $style.error],
|
||||||
[cell.selected ? $style.selected : {}],
|
[cell.selected ? $style.selected : {}],
|
||||||
[cell.ranged ? $style.ranged : {}],
|
[cell.ranged ? $style.ranged : {}],
|
||||||
[needsContentCentering ? $style.center : {}]
|
[needsContentCentering ? $style.center : {}],
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div v-if="!editing" ref="contentAreaEl">
|
<div v-if="!editing" ref="contentAreaEl">
|
||||||
|
@ -47,15 +48,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, ref, toRefs, watch } from 'vue';
|
import { computed, defineAsyncComponent, nextTick, ref, shallowRef, toRefs, watch } from 'vue';
|
||||||
import {
|
import { GridEventEmitter, Size } from '@/components/grid/grid.js';
|
||||||
CellValue,
|
import { useTooltip } from '@/scripts/use-tooltip.js';
|
||||||
equalCellAddress,
|
import * as os from '@/os.js';
|
||||||
getCellAddress,
|
import { CellValue, GridCell } from '@/components/grid/cell.js';
|
||||||
GridCell,
|
import { equalCellAddress, getCellAddress } from '@/components/grid/utils.js';
|
||||||
GridEventEmitter,
|
|
||||||
Size,
|
|
||||||
} from '@/components/grid/types.js';
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'operation:beginEdit', sender: GridCell): void;
|
(ev: 'operation:beginEdit', sender: GridCell): void;
|
||||||
|
@ -70,9 +68,9 @@ const props = defineProps<{
|
||||||
|
|
||||||
const { cell, bus } = toRefs(props);
|
const { cell, bus } = toRefs(props);
|
||||||
|
|
||||||
const rootEl = ref<InstanceType<typeof HTMLTableCellElement>>();
|
const rootEl = shallowRef<InstanceType<typeof HTMLTableCellElement>>();
|
||||||
const contentAreaEl = ref<InstanceType<typeof HTMLDivElement>>();
|
const contentAreaEl = shallowRef<InstanceType<typeof HTMLDivElement>>();
|
||||||
const inputAreaEl = ref<InstanceType<typeof HTMLDivElement>>();
|
const inputAreaEl = shallowRef<InstanceType<typeof HTMLDivElement>>();
|
||||||
|
|
||||||
const editing = ref<boolean>(false);
|
const editing = ref<boolean>(false);
|
||||||
const editingValue = ref<CellValue>(undefined);
|
const editingValue = ref<CellValue>(undefined);
|
||||||
|
@ -209,6 +207,19 @@ function emitContentSizeChanged() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useTooltip(rootEl, (showing) => {
|
||||||
|
if (cell.value.validation.valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = cell.value.validation.violations.map(it => it.result.message).join('\n');
|
||||||
|
os.popup(defineAsyncComponent(() => import('@/components/grid/MkCellTooltip.vue')), {
|
||||||
|
showing,
|
||||||
|
content,
|
||||||
|
targetElement: rootEl.value,
|
||||||
|
}, {}, 'closed');
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
|
@ -250,6 +261,10 @@ $cellHeight: 28px;
|
||||||
&.center {
|
&.center {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
border: solid 0.5px var(--error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
|
|
@ -19,9 +19,10 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { toRefs } from 'vue';
|
import { toRefs } from 'vue';
|
||||||
import { CellValue, GridCell, GridEventEmitter, GridRow, Size } from '@/components/grid/types.js';
|
import { GridEventEmitter, GridRow, Size } from '@/components/grid/grid.js';
|
||||||
import MkDataCell from '@/components/grid/MkDataCell.vue';
|
import MkDataCell from '@/components/grid/MkDataCell.vue';
|
||||||
import MkNumberCell from '@/components/grid/MkNumberCell.vue';
|
import MkNumberCell from '@/components/grid/MkNumberCell.vue';
|
||||||
|
import { CellValue, GridCell } from '@/components/grid/cell.js';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'operation:beginEdit', sender: GridCell): void;
|
(ev: 'operation:beginEdit', sender: GridCell): void;
|
||||||
|
|
|
@ -36,24 +36,21 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, toRefs, watch } from 'vue';
|
import { computed, ref, toRefs, watch } from 'vue';
|
||||||
import {
|
import {
|
||||||
calcCellWidth,
|
CellValueChangedEvent,
|
||||||
CELL_ADDRESS_NONE,
|
|
||||||
CellAddress,
|
|
||||||
CellValue, CellValueChangedEvent,
|
|
||||||
ColumnSetting,
|
ColumnSetting,
|
||||||
DataSource,
|
DataSource,
|
||||||
equalCellAddress,
|
|
||||||
getCellAddress,
|
|
||||||
GridCell,
|
|
||||||
GridColumn,
|
GridColumn,
|
||||||
GridEventEmitter,
|
GridEventEmitter,
|
||||||
GridRow,
|
GridRow,
|
||||||
GridState,
|
GridState,
|
||||||
Size,
|
Size,
|
||||||
} from '@/components/grid/types.js';
|
} from '@/components/grid/grid.js';
|
||||||
import MkDataRow from '@/components/grid/MkDataRow.vue';
|
import MkDataRow from '@/components/grid/MkDataRow.vue';
|
||||||
import MkHeaderRow from '@/components/grid/MkHeaderRow.vue';
|
import MkHeaderRow from '@/components/grid/MkHeaderRow.vue';
|
||||||
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||||
|
import { cellValidation, ValidateViolation } from '@/components/grid/cell-validators.js';
|
||||||
|
import { CELL_ADDRESS_NONE, CellAddress, CellValue, GridCell } from '@/components/grid/cell.js';
|
||||||
|
import { calcCellWidth, equalCellAddress, getCellAddress } from '@/components/grid/utils.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
columnSettings: ColumnSetting[],
|
columnSettings: ColumnSetting[],
|
||||||
|
@ -61,6 +58,7 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
(ev: 'operation:cellValidation', violation: ValidateViolation): void;
|
||||||
(ev: 'change:cellValue', event: CellValueChangedEvent): void;
|
(ev: 'change:cellValue', event: CellValueChangedEvent): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -526,10 +524,20 @@ function onHeaderCellWidthLargest(sender: GridColumn) {
|
||||||
|
|
||||||
function setCellValue(sender: GridCell | CellAddress, newValue: CellValue) {
|
function setCellValue(sender: GridCell | CellAddress, newValue: CellValue) {
|
||||||
const cellAddress = 'address' in sender ? sender.address : sender;
|
const cellAddress = 'address' in sender ? sender.address : sender;
|
||||||
cells.value[cellAddress.row][cellAddress.col].value = newValue;
|
const cell = cells.value[cellAddress.row][cellAddress.col];
|
||||||
|
|
||||||
|
const violation = cellValidation(cell, newValue);
|
||||||
|
emit('operation:cellValidation', violation);
|
||||||
|
|
||||||
|
cell.validation = {
|
||||||
|
valid: violation.valid,
|
||||||
|
violations: violation.violations.filter(it => !it.valid),
|
||||||
|
};
|
||||||
|
cell.value = newValue;
|
||||||
|
|
||||||
emit('change:cellValue', {
|
emit('change:cellValue', {
|
||||||
column: columns.value[cellAddress.col],
|
column: cell.column,
|
||||||
row: rows.value[cellAddress.row],
|
row: cell.row,
|
||||||
value: newValue,
|
value: newValue,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -709,6 +717,10 @@ function refreshData() {
|
||||||
selected: false,
|
selected: false,
|
||||||
ranged: false,
|
ranged: false,
|
||||||
contentSize: { width: 0, height: 0 },
|
contentSize: { width: 0, height: 0 },
|
||||||
|
validation: {
|
||||||
|
valid: true,
|
||||||
|
violations: [],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
rowCells.push(cell);
|
rowCells.push(cell);
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, ref, toRefs, watch } from 'vue';
|
import { computed, nextTick, ref, toRefs, watch } from 'vue';
|
||||||
import { GridColumn, GridEventEmitter, Size } from '@/components/grid/types.js';
|
import { GridColumn, GridEventEmitter, Size } from '@/components/grid/grid.js';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'operation:beginWidthChange', sender: GridColumn): void;
|
(ev: 'operation:beginWidthChange', sender: GridColumn): void;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { GridColumn, GridEventEmitter, Size } from '@/components/grid/types.js';
|
import { GridColumn, GridEventEmitter, Size } from '@/components/grid/grid.js';
|
||||||
import MkHeaderCell from '@/components/grid/MkHeaderCell.vue';
|
import MkHeaderCell from '@/components/grid/MkHeaderCell.vue';
|
||||||
import MkNumberCell from '@/components/grid/MkNumberCell.vue';
|
import MkNumberCell from '@/components/grid/MkNumberCell.vue';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { GridRow } from '@/components/grid/types.js';
|
import { GridRow } from '@/components/grid/grid.js';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
content: string,
|
content: string,
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { GridColumn, GridRow } from '@/components/grid/grid.js';
|
||||||
|
import { CellValue, GridCell } from '@/components/grid/cell.js';
|
||||||
|
|
||||||
|
export type ValidatorParams = {
|
||||||
|
column: GridColumn;
|
||||||
|
row: GridRow;
|
||||||
|
value: CellValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ValidatorResult = {
|
||||||
|
valid: boolean;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CellValidator = {
|
||||||
|
name?: string;
|
||||||
|
validate: (params: ValidatorParams) => ValidatorResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ValidateViolation = {
|
||||||
|
valid: boolean;
|
||||||
|
params: ValidatorParams;
|
||||||
|
violations: ValidateViolationItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ValidateViolationItem = {
|
||||||
|
valid: boolean;
|
||||||
|
validator: CellValidator;
|
||||||
|
result: ValidatorResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cellValidation(cell: GridCell, newValue: CellValue): ValidateViolation {
|
||||||
|
const { column, row } = cell;
|
||||||
|
const validators = column.setting.validators ?? [];
|
||||||
|
|
||||||
|
const params: ValidatorParams = {
|
||||||
|
column,
|
||||||
|
row,
|
||||||
|
value: newValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
const violations: ValidateViolationItem[] = validators.map(validator => {
|
||||||
|
const result = validator.validate(params);
|
||||||
|
return {
|
||||||
|
valid: result.valid,
|
||||||
|
validator,
|
||||||
|
result,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid: violations.every(v => v.result.valid),
|
||||||
|
params,
|
||||||
|
violations,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const required: CellValidator = {
|
||||||
|
name: 'required',
|
||||||
|
validate: (params: ValidatorParams): ValidatorResult => {
|
||||||
|
const { value } = params;
|
||||||
|
return {
|
||||||
|
valid: value !== null && value !== undefined && value !== '',
|
||||||
|
message: 'This field is required.',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { ValidateViolationItem } from '@/components/grid/cell-validators.js';
|
||||||
|
import { GridColumn, GridRow, Size } from '@/components/grid/grid.js';
|
||||||
|
|
||||||
|
export type CellValue = string | boolean | number | undefined | null
|
||||||
|
|
||||||
|
export type CellAddress = {
|
||||||
|
row: number;
|
||||||
|
col: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CELL_ADDRESS_NONE: CellAddress = {
|
||||||
|
row: -1,
|
||||||
|
col: -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GridCell = {
|
||||||
|
address: CellAddress;
|
||||||
|
value: CellValue;
|
||||||
|
column: GridColumn;
|
||||||
|
row: GridRow;
|
||||||
|
selected: boolean;
|
||||||
|
ranged: boolean;
|
||||||
|
contentSize: Size;
|
||||||
|
validation: {
|
||||||
|
valid: boolean;
|
||||||
|
violations: ValidateViolationItem[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { EventEmitter } from 'eventemitter3';
|
||||||
|
import { CellValidator } from '@/components/grid/cell-validators.js';
|
||||||
|
import { CellValue } from '@/components/grid/cell.js';
|
||||||
|
|
||||||
|
export type DataSource = Record<string, CellValue>;
|
||||||
|
|
||||||
|
export type GridState = 'normal' | 'cellSelecting' | 'cellEditing' | 'colResizing' | 'colSelecting' | 'rowSelecting'
|
||||||
|
|
||||||
|
export type Size = {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SizeStyle = number | 'auto' | undefined;
|
||||||
|
|
||||||
|
export type ColumnType = 'text' | 'number' | 'date' | 'boolean' | 'image';
|
||||||
|
|
||||||
|
export type ColumnSetting = {
|
||||||
|
bindTo: string;
|
||||||
|
title?: string;
|
||||||
|
type: ColumnType;
|
||||||
|
width: SizeStyle;
|
||||||
|
editable?: boolean;
|
||||||
|
validators?: CellValidator[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GridColumn = {
|
||||||
|
index: number;
|
||||||
|
setting: ColumnSetting;
|
||||||
|
width: string;
|
||||||
|
contentSize: Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GridRow = {
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CellValueChangedEvent = {
|
||||||
|
column: GridColumn;
|
||||||
|
row: GridRow;
|
||||||
|
value: CellValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GridEventEmitter extends EventEmitter<{}> {
|
||||||
|
}
|
|
@ -1,116 +0,0 @@
|
||||||
import { EventEmitter } from 'eventemitter3';
|
|
||||||
|
|
||||||
export type CellValue = string | boolean | number | undefined | null
|
|
||||||
|
|
||||||
export type DataSource = Record<string, CellValue>;
|
|
||||||
|
|
||||||
export type GridState = 'normal' | 'cellSelecting' | 'cellEditing' | 'colResizing' | 'colSelecting' | 'rowSelecting'
|
|
||||||
|
|
||||||
export type RowState = 'normal' | 'added' | 'deleted'
|
|
||||||
|
|
||||||
export type Size = {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SizeStyle = number | 'auto' | undefined;
|
|
||||||
|
|
||||||
export type CellAddress = {
|
|
||||||
row: number;
|
|
||||||
col: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CELL_ADDRESS_NONE: CellAddress = {
|
|
||||||
row: -1,
|
|
||||||
col: -1,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GridCell = {
|
|
||||||
address: CellAddress;
|
|
||||||
value: CellValue;
|
|
||||||
column: GridColumn;
|
|
||||||
row: GridRow;
|
|
||||||
selected: boolean;
|
|
||||||
ranged: boolean;
|
|
||||||
contentSize: Size;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ColumnType = 'text' | 'number' | 'date' | 'boolean' | 'image';
|
|
||||||
|
|
||||||
export type ColumnSetting = {
|
|
||||||
bindTo: string;
|
|
||||||
title?: string;
|
|
||||||
type: ColumnType;
|
|
||||||
width: SizeStyle;
|
|
||||||
editable?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GridColumn = {
|
|
||||||
index: number;
|
|
||||||
setting: ColumnSetting;
|
|
||||||
width: string;
|
|
||||||
contentSize: Size;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GridRow = {
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CellValueChangedEvent = {
|
|
||||||
column: GridColumn;
|
|
||||||
row: GridRow;
|
|
||||||
value: CellValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GridEventEmitter extends EventEmitter<{}> {
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isElement(elem: any): elem is HTMLElement {
|
|
||||||
return elem instanceof HTMLElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isCellElement(elem: any): elem is HTMLTableCellElement {
|
|
||||||
return elem instanceof HTMLTableCellElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isRowElement(elem: any): elem is HTMLTableRowElement {
|
|
||||||
return elem instanceof HTMLTableRowElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCellAddress(elem: HTMLElement, parentNodeCount = 10): CellAddress {
|
|
||||||
let node = elem;
|
|
||||||
for (let i = 0; i < parentNodeCount; i++) {
|
|
||||||
if (isCellElement(node) && isRowElement(node.parentElement)) {
|
|
||||||
return {
|
|
||||||
// ヘッダ行ぶんを除く
|
|
||||||
row: node.parentElement.rowIndex - 1,
|
|
||||||
// 数値列ぶんを除く
|
|
||||||
col: node.cellIndex - 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!node.parentElement) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
node = node.parentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CELL_ADDRESS_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function equalCellAddress(a: CellAddress, b: CellAddress): boolean {
|
|
||||||
return a.row === b.row && a.col === b.col;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function calcCellWidth(widthSetting: SizeStyle): string {
|
|
||||||
switch (widthSetting) {
|
|
||||||
case undefined:
|
|
||||||
case 'auto': {
|
|
||||||
return 'auto';
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return `${widthSetting}px`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { SizeStyle } from '@/components/grid/types.js';
|
||||||
|
import { CELL_ADDRESS_NONE, CellAddress } from '@/components/grid/cell.js';
|
||||||
|
|
||||||
|
export function isCellElement(elem: any): elem is HTMLTableCellElement {
|
||||||
|
return elem instanceof HTMLTableCellElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRowElement(elem: any): elem is HTMLTableRowElement {
|
||||||
|
return elem instanceof HTMLTableRowElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calcCellWidth(widthSetting: SizeStyle): string {
|
||||||
|
switch (widthSetting) {
|
||||||
|
case undefined:
|
||||||
|
case 'auto': {
|
||||||
|
return 'auto';
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return `${widthSetting}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCellAddress(elem: HTMLElement, parentNodeCount = 10): CellAddress {
|
||||||
|
let node = elem;
|
||||||
|
for (let i = 0; i < parentNodeCount; i++) {
|
||||||
|
if (isCellElement(node) && isRowElement(node.parentElement)) {
|
||||||
|
return {
|
||||||
|
// ヘッダ行ぶんを除く
|
||||||
|
row: node.parentElement.rowIndex - 1,
|
||||||
|
// 数値列ぶんを除く
|
||||||
|
col: node.cellIndex - 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.parentElement) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = node.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CELL_ADDRESS_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function equalCellAddress(a: CellAddress, b: CellAddress): boolean {
|
||||||
|
return a.row === b.row && a.col === b.col;
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
<MkGrid
|
<MkGrid
|
||||||
:data="convertedGridItems"
|
:data="convertedGridItems"
|
||||||
:columnSettings="columnSettings"
|
:columnSettings="columnSettings"
|
||||||
|
@operation:cellValidation="onCellValidation"
|
||||||
@change:cellValue="onChangeCellValue"
|
@change:cellValue="onChangeCellValue"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,7 +44,7 @@
|
||||||
v-if="gridItems.length > 0"
|
v-if="gridItems.length > 0"
|
||||||
:class="$style.buttons"
|
:class="$style.buttons"
|
||||||
>
|
>
|
||||||
<MkButton primary @click="onRegistryClicked">{{ i18n.ts.registration }}</MkButton>
|
<MkButton primary :disabled="registerButtonDisabled" @click="onRegistryClicked">{{ i18n.ts.registration }}</MkButton>
|
||||||
<MkButton @click="onClearClicked">{{ i18n.ts.clear }}</MkButton>
|
<MkButton @click="onClearClicked">{{ i18n.ts.clear }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,7 +57,7 @@ import * as Misskey from 'misskey-js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { GridItem, IGridItem } from '@/pages/admin/custom-emojis-grid.impl.js';
|
import { GridItem, IGridItem } from '@/pages/admin/custom-emojis-grid.impl.js';
|
||||||
import MkGrid from '@/components/grid/MkGrid.vue';
|
import MkGrid from '@/components/grid/MkGrid.vue';
|
||||||
import { CellValueChangedEvent, ColumnSetting } from '@/components/grid/types.js';
|
import { CellValueChangedEvent, ColumnSetting } from '@/components/grid/grid.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import { uploadFile } from '@/scripts/upload.js';
|
import { uploadFile } from '@/scripts/upload.js';
|
||||||
|
@ -65,6 +66,7 @@ import { defaultStore } from '@/store.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
import { required, ValidateViolation } from '@/components/grid/cell-validators.js';
|
||||||
|
|
||||||
type FolderItem = {
|
type FolderItem = {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -78,8 +80,8 @@ const emit = defineEmits<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const columnSettings: ColumnSetting[] = [
|
const columnSettings: ColumnSetting[] = [
|
||||||
{ bindTo: 'url', title: '🎨', type: 'image', editable: false, width: 50 },
|
{ bindTo: 'url', title: '🎨', type: 'image', editable: false, width: 50, validators: [required] },
|
||||||
{ bindTo: 'name', title: 'name', type: 'text', editable: true, width: 140 },
|
{ bindTo: 'name', title: 'name', type: 'text', editable: true, width: 140, validators: [required] },
|
||||||
{ bindTo: 'category', title: 'category', type: 'text', editable: true, width: 140 },
|
{ bindTo: 'category', title: 'category', type: 'text', editable: true, width: 140 },
|
||||||
{ bindTo: 'aliases', title: 'aliases', type: 'text', editable: true, width: 140 },
|
{ bindTo: 'aliases', title: 'aliases', type: 'text', editable: true, width: 140 },
|
||||||
{ bindTo: 'license', title: 'license', type: 'text', editable: true, width: 140 },
|
{ bindTo: 'license', title: 'license', type: 'text', editable: true, width: 140 },
|
||||||
|
@ -92,6 +94,8 @@ const uploadFolders = ref<FolderItem[]>([]);
|
||||||
const gridItems = ref<IGridItem[]>([]);
|
const gridItems = ref<IGridItem[]>([]);
|
||||||
const selectedFolderId = ref(defaultStore.state.uploadFolder);
|
const selectedFolderId = ref(defaultStore.state.uploadFolder);
|
||||||
const keepOriginalUploading = ref(defaultStore.state.keepOriginalUploading);
|
const keepOriginalUploading = ref(defaultStore.state.keepOriginalUploading);
|
||||||
|
const registerButtonDisabled = ref<boolean>(false);
|
||||||
|
|
||||||
const convertedGridItems = computed(() => gridItems.value.map(it => it as Record<string, any>));
|
const convertedGridItems = computed(() => gridItems.value.map(it => it as Record<string, any>));
|
||||||
|
|
||||||
async function onRegistryClicked() {
|
async function onRegistryClicked() {
|
||||||
|
@ -180,6 +184,11 @@ async function onDrop(ev: DragEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onCellValidation(violation: ValidateViolation) {
|
||||||
|
console.log(violation);
|
||||||
|
registerButtonDisabled.value = !violation.valid;
|
||||||
|
}
|
||||||
|
|
||||||
function onChangeCellValue(event: CellValueChangedEvent) {
|
function onChangeCellValue(event: CellValueChangedEvent) {
|
||||||
console.log(event);
|
console.log(event);
|
||||||
const item = gridItems.value[event.row.index];
|
const item = gridItems.value[event.row.index];
|
||||||
|
|
Loading…
Reference in New Issue