mirror of
https://github.com/hangeol-chang/obsidian-csv-allinone.git
synced 2026-07-22 05:37:25 +00:00
[FEAT] header 바꾼거 정리중
This commit is contained in:
parent
d7d677380d
commit
fd8f994187
4 changed files with 124 additions and 34 deletions
4
main.ts
4
main.ts
|
|
@ -34,8 +34,8 @@ export default class CsvPlugin extends Plugin {
|
|||
createCsvTableView = (csvTable: CSVTable): HTMLElement => {
|
||||
return createCsvTableView_(csvTable);
|
||||
}
|
||||
createCsvFile = (filename: string, filePath: string, columnData: { name:string, type: string }[]) => {
|
||||
createCsvFile_(this.app, filename, filePath, columnData);
|
||||
createCsvFile = (filename: string, columnData: Header) => {
|
||||
createCsvFile_(this.app, filename, columnData);
|
||||
}
|
||||
|
||||
async onload() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { App, Modal, Notice, Plugin } from 'obsidian';
|
||||
import './styles/csvCreator.css';
|
||||
import { columnTypes } from './types';
|
||||
import { CSVCellType, CSVCellTypeString, Header } from './types';
|
||||
|
||||
// dataviewjs 등으로 자동적으로 파일을 생성하게 할 때 사용할 수 있는 api
|
||||
export const createCsvFile_ = (app: App, filename: string, filePath: string, columnData: { name:string, type: string }[]) : boolean => {
|
||||
export const createCsvFile_ = (app: App, filename: string, columnData: Header) : boolean => {
|
||||
// 수정해야함.
|
||||
// .csv 내용 생성 (헤더만 추가)
|
||||
const csvContent = columnData.map((col) => col.name).join(',') + '\n';
|
||||
|
||||
|
|
@ -19,20 +20,18 @@ export const createCsvFile_ = (app: App, filename: string, filePath: string, col
|
|||
|
||||
// 파일 저장
|
||||
const vault = app.vault;
|
||||
const fullPathCsv = `${filePath}/${filename}.csv`;
|
||||
const fullPathMeta = `${filePath}/${filename}.csv.meta`;
|
||||
|
||||
// .csv 파일 저장
|
||||
vault.adapter.write(fullPathCsv, csvContent).then(() => {
|
||||
new Notice(`${fullPathCsv} file has been created.`);
|
||||
vault.adapter.write(`${filename}.csv`, csvContent).then(() => {
|
||||
new Notice(`${filename}.csv file has been created.`);
|
||||
}).catch((err) => {
|
||||
new Notice(`Error occurred while creating .csv file:\n${err}`);
|
||||
return false;
|
||||
});
|
||||
|
||||
// .csv.meta 파일 저장
|
||||
vault.adapter.write(fullPathMeta, metaContent).then(() => {
|
||||
new Notice(`${fullPathMeta} file has been created.`);
|
||||
vault.adapter.write(`${filename}.csv.meta`, metaContent).then(() => {
|
||||
new Notice(`${filename}.csv.meta file has been created.`);
|
||||
}).catch((err) => {
|
||||
new Notice(`Error occurred while creating .csv.meta file:\n${err}`);
|
||||
return false;
|
||||
|
|
@ -44,25 +43,40 @@ export default class CsvCreateModal extends Modal {
|
|||
|
||||
columnsWrapper: HTMLElement;
|
||||
|
||||
constructor(app: App) {
|
||||
super(app);
|
||||
}
|
||||
constructor(app: App) { super(app); }
|
||||
|
||||
// column 추가
|
||||
addColumn() {
|
||||
let inputWarpper = this.columnsWrapper.createEl('tr');
|
||||
let columnTd = inputWarpper.createEl('td');
|
||||
let columnInput = columnTd.createEl('input');
|
||||
columnInput.classList.add('colname-input');
|
||||
columnInput.id = 'colname';
|
||||
columnInput.placeholder = 'name';
|
||||
|
||||
let typeTd = inputWarpper.createEl('td');
|
||||
let typeSelect = typeTd.createEl('select');
|
||||
typeSelect.classList.add('coltype-select');
|
||||
for (let type of columnTypes) {
|
||||
typeSelect.id = 'coltype';
|
||||
for (let type of CSVCellTypeString) {
|
||||
let option = typeSelect.createEl('option');
|
||||
option.value = type;
|
||||
option.text = type;
|
||||
};
|
||||
|
||||
let defaultTd = inputWarpper.createEl('td');
|
||||
let defaultInput = defaultTd.createEl('input');
|
||||
defaultInput.classList.add('coldefault');
|
||||
defaultInput.id = 'coldefault';
|
||||
defaultInput.placeholder = 'default value';
|
||||
|
||||
// select type일 때만 options 추가
|
||||
let optionsTd = inputWarpper.createEl('td');
|
||||
let optionsInput = optionsTd.createEl('input');
|
||||
optionsInput.classList.add('coloptions-wrapper');
|
||||
optionsInput.id = 'coloptions';
|
||||
optionsInput.placeholder = 'options (separated by comma)';
|
||||
// type가 select일 때만 css로 쿼리해서 보이게 할 것.
|
||||
}
|
||||
|
||||
createCsvFile() {
|
||||
|
|
@ -88,6 +102,47 @@ export default class CsvCreateModal extends Modal {
|
|||
filePath = filePath.slice(0, -1);
|
||||
}
|
||||
|
||||
const columnData: Header = {};
|
||||
this.columnsWrapper.querySelectorAll('tr').forEach((columnEl) => {
|
||||
const inputEl = columnEl.querySelector('#colname') as HTMLInputElement;
|
||||
const selectEl = columnEl.querySelector('#coltype') as HTMLSelectElement;
|
||||
const defaultEl = columnEl.querySelector('#coldefault') as HTMLInputElement;
|
||||
const optionsEl = columnEl.querySelector('#coloptions') as HTMLInputElement;
|
||||
|
||||
const columnName = inputEl?.value.trim();
|
||||
const columnType = selectEl?.value;
|
||||
let columnDefault : string | number = defaultEl?.value.trim();
|
||||
|
||||
if(columnDefault == '') {
|
||||
switch(columnType) {
|
||||
case 'number':
|
||||
columnDefault = 0;
|
||||
break;
|
||||
case 'stringDate':
|
||||
columnDefault = '1970-01-01';
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
case 'select':
|
||||
case 'null':
|
||||
default:
|
||||
columnDefault = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (columnName && columnType) {
|
||||
columnData[columnName] = {
|
||||
type: columnType as any,
|
||||
default: columnDefault,
|
||||
options:
|
||||
columnType === 'select'
|
||||
? optionsEl.value.split(',').map((option) => option.trim())
|
||||
: undefined
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 컬럼 정보 수집
|
||||
const columnData: { name: string; type: string }[] = [];
|
||||
this.columnsWrapper.querySelectorAll('tr').forEach((columnEl) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { CSVTable, Header } from './types'
|
||||
import { App, TFile } from 'obsidian';
|
||||
import { read } from 'fs';
|
||||
import { CSVTable, Header, isHeaderSame } from './types'
|
||||
import { App, Notice, TFile } from 'obsidian';
|
||||
|
||||
// meta file을 수정하는 로직이 포함되어야 함.
|
||||
|
||||
|
|
@ -10,28 +11,26 @@ export const readCSV_ = async (app: App, fileName: string): Promise<CSVTable | n
|
|||
const csvContent = await loadFile(app, fileName); // 파일 로드.
|
||||
|
||||
const lines = csvContent.split("\n").map(line => line.trim());
|
||||
const headersString = lines.shift()?.split(",") ?? [];
|
||||
const headersString: string[] = lines.shift()?.split(",") ?? [];
|
||||
const rows = lines.map(line => line.split(",").map(cell => cell.trim()));
|
||||
|
||||
// read .csv.meta file,
|
||||
// meta file 존재 여부 확인해서 없으면 생성.
|
||||
if(await app.vault.adapter.exists(`${fileName}.meta`) == false) {
|
||||
const headers: { name: string, type: string }[] =
|
||||
headersString.map((header) => ({ name: header, type: "string" }));
|
||||
|
||||
const metaContent = JSON.stringify(
|
||||
headers.reduce((acc, col) => {
|
||||
acc[col.name] = col.type;
|
||||
return acc;
|
||||
}, {} as Record<string, string>),
|
||||
null,
|
||||
2
|
||||
const metaContent: string = (
|
||||
JSON.stringify(headersString.reduce((meta, header: string) => {
|
||||
meta[header] = {
|
||||
type: "string",
|
||||
default: ""
|
||||
};
|
||||
return meta;
|
||||
}, {} as Header))
|
||||
);
|
||||
// const metaContent = JSON.stringify(headers, null, 2);
|
||||
|
||||
await saveFile(app, `${fileName}.meta`, metaContent);
|
||||
new Notice(`meta file created : ${fileName}.meta`);
|
||||
}
|
||||
|
||||
|
||||
// meta file 로드.
|
||||
const metaData = await loadFile(app, `${fileName}.meta`); // 메타 파일 로드.
|
||||
const headers: Header = Header(metaData); // 메타 파일 파싱.
|
||||
|
|
@ -47,8 +46,9 @@ export const saveCSV_ = async (app: App, fileName: string, table: CSVTable): Pro
|
|||
// contents, meta로 분리 필요.
|
||||
const content = table.toCSV();
|
||||
await saveFile(app, fileName, content);
|
||||
// const meta = table.getMeta();
|
||||
// await saveMetaFile(app, `${fileName}.meta`, meta);
|
||||
|
||||
const header = table.getHeaders();
|
||||
await saveMetaFile(app, `${fileName}.meta`, header);
|
||||
} else {
|
||||
console.error(`file extension is not csv : ${fileName} (save)`);
|
||||
}
|
||||
|
|
@ -70,8 +70,16 @@ const saveFile = async (app: App, fileName: string, content: string): Promise<vo
|
|||
await vault.create(fileName, content);
|
||||
}
|
||||
}
|
||||
const saveMetaFile = async (app: App, fileName: string, content: Header[]): Promise<void> => {
|
||||
const saveMetaFile = async (app: App, fileName: string, header1: Header): Promise<void> => {
|
||||
const metaData = await loadFile(app, fileName); // 메타 파일 로드.
|
||||
const header2: Header = Header(metaData); // 메타 파일 파싱.
|
||||
|
||||
if(!isHeaderSame(header1, header2)) {
|
||||
const metaContent: string = (
|
||||
JSON.stringify(header1)
|
||||
);
|
||||
await saveFile(app, fileName, metaContent);
|
||||
}
|
||||
}
|
||||
|
||||
const loadFile = async (app: App, fileName: string): Promise<string> => {
|
||||
|
|
|
|||
31
src/types.ts
31
src/types.ts
|
|
@ -6,7 +6,8 @@ function isStringDate(date: string): date is stringDate {
|
|||
export type select = string;
|
||||
|
||||
export type CSVCell = string | number | stringDate | select | null;
|
||||
export type CSVCellType = "string" | "number" | "stringDate" | "select" | "null";
|
||||
export const CSVCellTypeString = ["string", "number", "stringDate", "select", "null"] as const;
|
||||
export type CSVCellType = (typeof CSVCellTypeString)[number]; // 문자열 리터럴 타입 생성
|
||||
export type CSVRow = CSVCell[];
|
||||
|
||||
export type Header = {
|
||||
|
|
@ -21,7 +22,7 @@ export function Header(metaData: string): Header {
|
|||
try {
|
||||
const headers: Header = Object.entries(parsedData).reduce((acc, [name, config]) => {
|
||||
acc[name] = {
|
||||
type: config.type as "string" | "number" | "stringDate" | "select", // type이 정확히 무엇인지 명시
|
||||
type: config.type as CSVCellType, // type이 정확히 무엇인지 명시
|
||||
default: config.default,
|
||||
...(config.options && { options: config.options }) // select일 때만 options 포함
|
||||
};
|
||||
|
|
@ -35,6 +36,32 @@ export function Header(metaData: string): Header {
|
|||
return {};
|
||||
}
|
||||
}
|
||||
export function isHeaderSame(header1: Header, header2: Header): boolean {
|
||||
if (Object.keys(header1).length !== Object.keys(header2).length) {
|
||||
return false;
|
||||
}
|
||||
for (const key in header1) {
|
||||
if (!header2[key] || header1[key].type !== header2[key].type) {
|
||||
return false;
|
||||
}
|
||||
if (header1[key].type === "select") {
|
||||
if (header1[key].options?.length !== header2[key].options?.length) {
|
||||
return false;
|
||||
}
|
||||
if(!header1[key].options || !header2[key].options) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < header1[key].options.length; i++) {
|
||||
if (header1[key].options[i] !== header2[key].options[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 두
|
||||
return true;
|
||||
}
|
||||
|
||||
export class CSVTable {
|
||||
private headers: Header; // Header는 이제 객체로 변경
|
||||
|
|
|
|||
Loading…
Reference in a new issue