Vanessa 2023-09-22 12:11:56 +08:00
parent 2a6597f388
commit 67574984d0
6 changed files with 299 additions and 36 deletions

View file

@ -191,6 +191,13 @@
opacity: 1; opacity: 1;
background-color: var(--b3-theme-background-light) !important; background-color: var(--b3-theme-background-light) !important;
} }
.av__cellassetimg {
max-height: 24px;
border-radius: var(--b3-border-radius);
margin: 1px 2px;
max-width: none;
}
} }
&__cellheader { &__cellheader {

View file

@ -0,0 +1,185 @@
import {Menu} from "../../../plugin/Menu";
import {transaction} from "../../wysiwyg/transaction";
import {updateAttrViewCellAnimation} from "./action";
import {isMobile} from "../../../util/functions";
import {Constants} from "../../../constants";
import {uploadFiles} from "../../upload";
import {pathPosix} from "../../../util/pathName";
export const bindAssetEvent = (options: {
protyle: IProtyle,
data: IAV,
menuElement: HTMLElement,
cellElements: HTMLElement[]
}) => {
options.menuElement.querySelector("input").addEventListener("change", (event: InputEvent & {
target: HTMLInputElement
}) => {
if (event.target.files.length === 0) {
return;
}
uploadFiles(options.protyle, event.target.files, event.target, (res) => {
const resData = JSON.parse(res)
const value: IAVCellAssetValue[] = []
Object.keys(resData.data.succMap).forEach((key) => {
value.push({
name: key,
content: resData.data.succMap[key],
type: Constants.SIYUAN_ASSETS_IMAGE.includes(pathPosix().extname(resData.data.succMap[key]).toLowerCase()) ? "image" : "file"
})
})
updateAssetCell({
protyle: options.protyle,
data: options.data,
cellElements: options.cellElements,
value
})
});
});
};
export const getAssetHTML = (data: IAVTable, cellElements: HTMLElement[]) => {
const cellId = cellElements[0].dataset.id;
const rowId = cellElements[0].parentElement.dataset.id;
let cellData: IAVCell
data.rows.find(row => {
if (row.id === rowId) {
row.cells.find(cell => {
if (cell.id === cellId) {
cellData = cell
return true
}
})
return true
}
})
let html = ""
if (cellData?.value?.mAsset) {
cellData.value.mAsset.forEach(item => {
if (!item.content) {
return
}
let contentHTML
if (item.type === "image") {
contentHTML = `<img style="max-height: 180px;max-width: 360px;border-radius: var(--b3-border-radius);margin: 4px 0;" src="${item.content}"/>`
} else {
contentHTML = `<span class="fn__ellipsis b3-menu__label" style="max-width: 360px">${item.name}</span>`
}
html += `<button data-type="addColOptionOrCell" class="b3-menu__item" draggable="true">
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
${contentHTML}
<svg class="b3-menu__action" data-type="setColOption"><use xlink:href="#iconEdit"></use></svg>
</button>`
})
}
return `<div class="b3-menu__items">
${html}
<button class="b3-menu__item">
<svg class="b3-menu__icon"><use xlink:href="#iconDownload"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.insertAsset}</span>
<input multiple class="b3-form__upload" type="file">
</button>
<button data-type="addAssetLink" class="b3-menu__item">
<svg class="b3-menu__icon"><use xlink:href="#iconLink"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.link}</span>
</button>
</div>`;
};
const updateAssetCell = (options: {
protyle: IProtyle,
data: IAV,
cellElements: HTMLElement[],
value: IAVCellAssetValue[]
}) => {
if (!options.value || options.value.length === 0 || !options.value[0].content) {
return;
}
let cellIndex = 0;
Array.from(options.cellElements[0].parentElement.querySelectorAll(".av__cell")).find((item: HTMLElement, index) => {
if (item.dataset.id === options.cellElements[0].dataset.id) {
cellIndex = index;
return true;
}
});
const colId = options.cellElements[0].dataset.colId;
const cellDoOperations: IOperation[] = [];
const cellUndoOperations: IOperation[] = [];
options.cellElements.forEach(item => {
let cellData: IAVCell;
const rowID = item.parentElement.dataset.id;
options.data.view.rows.find(row => {
if (row.id === rowID) {
cellData = row.cells[cellIndex];
// 为空时 cellId 每次请求都不一致
cellData.id = item.dataset.id;
if (!cellData.value || !cellData.value.mAsset) {
cellData.value = {mAsset: []} as IAVCellValue;
}
return true;
}
});
const oldValue = Object.assign([], cellData.value.mAsset);
options.value.forEach(item => {
cellData.value.mAsset.push(item);
})
cellDoOperations.push({
action: "updateAttrViewCell",
id: cellData.id,
keyID: colId,
rowID,
avID: options.data.id,
data: cellData.value
});
cellUndoOperations.push({
action: "updateAttrViewCell",
id: cellData.id,
keyID: colId,
rowID,
avID: options.data.id,
data: {
mAsset: oldValue
}
});
updateAttrViewCellAnimation(item);
});
transaction(options.protyle, cellDoOperations, cellUndoOperations);
}
export const addAssetLink = (protyle: IProtyle, data: IAV, cellElements: HTMLElement[], target: HTMLElement) => {
const menu = new Menu("av-asset-link", () => {
const textElements = menu.element.querySelectorAll("textarea")
if (!textElements[0].value) {
return
}
updateAssetCell({
protyle,
data,
cellElements,
value: [{
type: "file",
name: textElements[1].value,
content: textElements[0].value,
}]
})
})
menu.addItem({
iconHTML: "",
label: `<textarea rows="1" style="margin:4px 0;width: ${isMobile() ? "200" : "360"}px" class="b3-text-field" placeholder="${window.siyuan.languages.link}"></textarea>`,
});
menu.addItem({
iconHTML: "",
label: `<textarea rows="1" style="margin:4px 0;width: ${isMobile() ? "200" : "360"}px" class="b3-text-field" placeholder="${window.siyuan.languages.title}"></textarea>`,
});
const rect = target.getBoundingClientRect();
menu.open({
x: rect.right,
y: rect.bottom,
w: rect.width,
h: rect.height,
});
}

View file

@ -356,6 +356,9 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[]) => {
} else if (["select", "mSelect"].includes(type) && blockElement) { } else if (["select", "mSelect"].includes(type) && blockElement) {
openMenuPanel({protyle, blockElement, type: "select", cellElements}); openMenuPanel({protyle, blockElement, type: "select", cellElements});
return; return;
} else if (type === "mAsset" && blockElement) {
openMenuPanel({protyle, blockElement, type: "asset", cellElements});
return;
} else if (type === "date" && blockElement) { } else if (type === "date" && blockElement) {
openMenuPanel({protyle, blockElement, type: "date", cellElements}); openMenuPanel({protyle, blockElement, type: "date", cellElements});
return; return;

View file

@ -10,11 +10,12 @@ import {addSort, bindSortsEvent, getSortsHTML} from "./sort";
import {bindDateEvent, getDateHTML, setDateValue} from "./date"; import {bindDateEvent, getDateHTML, setDateValue} from "./date";
import {formatNumber} from "./number"; import {formatNumber} from "./number";
import {removeAttrViewColAnimation} from "./action"; import {removeAttrViewColAnimation} from "./action";
import {addAssetLink, bindAssetEvent, getAssetHTML} from "./asset";
export const openMenuPanel = (options: { export const openMenuPanel = (options: {
protyle: IProtyle, protyle: IProtyle,
blockElement: Element, blockElement: Element,
type: "select" | "properties" | "config" | "sorts" | "filters" | "edit" | "date", type: "select" | "properties" | "config" | "sorts" | "filters" | "edit" | "date" | "asset",
colId?: string, // for edit colId?: string, // for edit
cellElements?: HTMLElement[] // for select & date cellElements?: HTMLElement[] // for select & date
}) => { }) => {
@ -42,6 +43,8 @@ export const openMenuPanel = (options: {
html = getFiltersHTML(data.view); html = getFiltersHTML(data.view);
} else if (options.type === "select") { } else if (options.type === "select") {
html = getSelectHTML(data.view, options.cellElements); html = getSelectHTML(data.view, options.cellElements);
} else if (options.type === "asset") {
html = getAssetHTML(data.view, options.cellElements);
} else if (options.type === "edit") { } else if (options.type === "edit") {
html = getEditHTML({protyle: options.protyle, data, colId: options.colId}); html = getEditHTML({protyle: options.protyle, data, colId: options.colId});
} else if (options.type === "date") { } else if (options.type === "date") {
@ -55,20 +58,21 @@ export const openMenuPanel = (options: {
avPanelElement = document.querySelector(".av__panel"); avPanelElement = document.querySelector(".av__panel");
const menuElement = avPanelElement.lastElementChild as HTMLElement; const menuElement = avPanelElement.lastElementChild as HTMLElement;
const tabRect = options.blockElement.querySelector(".layout-tab-bar").getBoundingClientRect(); const tabRect = options.blockElement.querySelector(".layout-tab-bar").getBoundingClientRect();
if (["select", "date", "asset"].includes(options.type)) {
const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect();
setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
if (options.type === "select") { if (options.type === "select") {
const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect();
setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
bindSelectEvent(options.protyle, data, menuElement, options.cellElements); bindSelectEvent(options.protyle, data, menuElement, options.cellElements);
const inputElement = menuElement.querySelector("input");
inputElement.select();
inputElement.focus();
} else if (options.type === "date") { } else if (options.type === "date") {
const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect();
setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
bindDateEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements}); bindDateEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements});
} else if (options.type === "asset") {
bindAssetEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements});
}
if (["select", "date"].includes(options.type)) {
const inputElement = menuElement.querySelector("input"); const inputElement = menuElement.querySelector("input");
inputElement.select(); inputElement.select();
inputElement.focus(); inputElement.focus();
}
} else { } else {
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height); setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
if (options.type === "sorts") { if (options.type === "sorts") {
@ -630,6 +634,11 @@ export const openMenuPanel = (options: {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
break; break;
} else if (type === "addAssetLink") {
addAssetLink(options.protyle, data, options.cellElements, target)
event.preventDefault();
event.stopPropagation();
break;
} else if (type === "clearDate") { } else if (type === "clearDate") {
setDateValue({ setDateValue({
cellElements: options.cellElements, cellElements: options.cellElements,

View file

@ -101,7 +101,7 @@ style="width: ${column.width || "200px"}">${getCalcValue(column) || '<svg><use x
} else if (cell.valueType === "mAsset") { } else if (cell.valueType === "mAsset") {
cell.value?.mAsset?.forEach((item) => { cell.value?.mAsset?.forEach((item) => {
if (item.type === "image") { if (item.type === "image") {
text += `<span class="b3-chip b3-chip--middle" style="background-image:url("${item.content}")"></span>`; text += `<img class="av__cellassetimg" src="${item.content}">`;
} else { } else {
text += `<span class="b3-chip b3-chip--middle" data-url="${item.content}">${item.name}</span>`; text += `<span class="b3-chip b3-chip--middle" data-url="${item.content}">${item.name}</span>`;
} }

View file

@ -84,7 +84,9 @@ interface Window {
dataLayer: any[] dataLayer: any[]
siyuan: ISiyuan siyuan: ISiyuan
webkit: any webkit: any
html2canvas: (element: Element, opitons: { useCORS: boolean }) => Promise<any>; html2canvas: (element: Element, opitons: {
useCORS: boolean
}) => Promise<any>;
JSAndroid: { JSAndroid: {
returnDesktop(): void returnDesktop(): void
openExternal(url: string): void openExternal(url: string): void
@ -237,7 +239,10 @@ interface IBackStack {
}, },
scrollTop?: number, scrollTop?: number,
callback?: string[], callback?: string[],
position?: { start: number, end: number } position?: {
start: number,
end: number
}
// 仅桌面端 // 仅桌面端
protyle?: IProtyle, protyle?: IProtyle,
zoomId?: string zoomId?: string
@ -269,14 +274,18 @@ interface INotebook {
interface ISiyuan { interface ISiyuan {
zIndex: number zIndex: number
storage?: { [key: string]: any }, storage?: {
[key: string]: any
},
printWin?: import("electron").BrowserWindow printWin?: import("electron").BrowserWindow
transactions?: { transactions?: {
protyle: IProtyle, protyle: IProtyle,
doOperations: IOperation[], doOperations: IOperation[],
undoOperations: IOperation[] undoOperations: IOperation[]
}[] }[]
reqIds: { [key: string]: number }, reqIds: {
[key: string]: number
},
editorIsFullscreen?: boolean, editorIsFullscreen?: boolean,
hideBreadcrumb?: boolean, hideBreadcrumb?: boolean,
notebooks?: INotebook[], notebooks?: INotebook[],
@ -300,7 +309,11 @@ interface ISiyuan {
userSiYuanSubscriptionType: number // 0 年付1 终生2 月付 userSiYuanSubscriptionType: number // 0 年付1 终生2 月付
userSiYuanSubscriptionStatus: number // -1未订阅0订阅可用1订阅封禁2订阅过期 userSiYuanSubscriptionStatus: number // -1未订阅0订阅可用1订阅封禁2订阅过期
userToken: string userToken: string
userTitles: { name: string, icon: string, desc: string }[] userTitles: {
name: string,
icon: string,
desc: string
}[]
}, },
dragElement?: HTMLElement, dragElement?: HTMLElement,
layout?: { layout?: {
@ -395,7 +408,10 @@ interface ILayoutJSON extends ILayoutOptions {
interface IDockTab { interface IDockTab {
type: string; type: string;
size: { width: number, height: number } size: {
width: number,
height: number
}
show: boolean show: boolean
icon: string icon: string
title: string title: string
@ -425,7 +441,10 @@ interface IPluginData {
interface IPluginDockTab { interface IPluginDockTab {
position: TPluginDockPosition, position: TPluginDockPosition,
size: { width: number, height: number }, size: {
width: number,
height: number
},
icon: string, icon: string,
hotkey?: string, hotkey?: string,
title: string, title: string,
@ -666,7 +685,10 @@ interface IConfig {
localIPs: string[] localIPs: string[]
readonly: boolean // 全局只读 readonly: boolean // 全局只读
uiLayout: Record<string, any> uiLayout: Record<string, any>
langs: { label: string, name: string }[] langs: {
label: string,
name: string
}[]
appearance: IAppearance appearance: IAppearance
editor: IEditor, editor: IEditor,
fileTree: IFileTree fileTree: IFileTree
@ -761,13 +783,25 @@ interface IKeymap {
[key: string]: IKeymapItem [key: string]: IKeymapItem
} }
} }
general: { [key: string]: IKeymapItem } general: {
[key: string]: IKeymapItem
}
editor: { editor: {
general: { [key: string]: IKeymapItem } general: {
insert: { [key: string]: IKeymapItem } [key: string]: IKeymapItem
heading: { [key: string]: IKeymapItem } }
list: { [key: string]: IKeymapItem } insert: {
table: { [key: string]: IKeymapItem } [key: string]: IKeymapItem
}
heading: {
[key: string]: IKeymapItem
}
list: {
[key: string]: IKeymapItem
}
table: {
[key: string]: IKeymapItem
}
} }
} }
@ -964,16 +998,41 @@ interface IAVCell {
valueType: TAVCol, valueType: TAVCol,
} }
interface IAVCellAssetValue {
content: string,
name: string,
type: "file" | "image"
}
interface IAVCellValue { interface IAVCellValue {
type?: TAVCol, type?: TAVCol,
text?: { content: string }, text?: {
number?: { content?: number, isNotEmpty: boolean, format?: string, formattedContent?: string }, content: string
mSelect?: { content: string, color: string }[] },
mAsset?: { content: string, name: string, type: "file" | "image" }[] number?: {
block?: { content: string, id?: string } content?: number,
url?: { content: string } isNotEmpty: boolean,
phone?: { content: string } format?: string,
email?: { content: string } formattedContent?: string
},
mSelect?: {
content: string,
color: string
}[]
mAsset?: IAVCellAssetValue[]
block?: {
content: string,
id?: string
}
url?: {
content: string
}
phone?: {
content: string
}
email?: {
content: string
}
date?: IAVCellDateValue date?: IAVCellDateValue
} }