remove solid; rework season search
This commit is contained in:
parent
ee985419e0
commit
21ad8c59d0
17 changed files with 407 additions and 357 deletions
|
|
@ -1,24 +0,0 @@
|
|||
import { onMount, Show } from 'solid-js';
|
||||
import { setIcon } from 'obsidian';
|
||||
|
||||
interface IconProps {
|
||||
iconName?: string;
|
||||
}
|
||||
|
||||
export default function Icon(props: IconProps) {
|
||||
let iconEl: HTMLDivElement | undefined;
|
||||
|
||||
onMount(() => {
|
||||
if (iconEl) {
|
||||
setIcon(iconEl, props.iconName || '');
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Show when={(props.iconName || '').length > 0}>
|
||||
<div class="icon-wrapper">
|
||||
<div ref={iconEl} class="icon"></div>
|
||||
</div>
|
||||
</Show>
|
||||
);
|
||||
}
|
||||
159
packages/obsidian/src/settings/PropertyMappingModelComponent.ts
Normal file
159
packages/obsidian/src/settings/PropertyMappingModelComponent.ts
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import { Component, setIcon } from 'obsidian';
|
||||
import type { PropertyMappingModelData } from 'packages/obsidian/src/settings/PropertyMapping';
|
||||
import { PropertyMappingModel, PropertyMappingOption, propertyMappingOptions } from 'packages/obsidian/src/settings/PropertyMapping';
|
||||
import { capitalizeFirstLetter } from 'packages/obsidian/src/utils/Utils';
|
||||
|
||||
interface PropertyMappingModelComponentProps {
|
||||
model: PropertyMappingModelData;
|
||||
save: (model: PropertyMappingModelData) => void;
|
||||
}
|
||||
|
||||
export default class PropertyMappingModelComponent extends Component {
|
||||
private readonly containerEl: HTMLElement;
|
||||
private readonly save: (model: PropertyMappingModelData) => void;
|
||||
private modelData: PropertyMappingModelData;
|
||||
private unsavedChanges = false;
|
||||
|
||||
constructor(containerEl: HTMLElement, props: PropertyMappingModelComponentProps) {
|
||||
super();
|
||||
this.containerEl = containerEl;
|
||||
this.modelData = props.model;
|
||||
this.save = props.save;
|
||||
}
|
||||
|
||||
override onload(): void {
|
||||
this.render();
|
||||
}
|
||||
|
||||
override onunload(): void {
|
||||
this.containerEl.empty();
|
||||
}
|
||||
|
||||
private get validationResult(): { res: boolean; err?: Error } {
|
||||
const model = PropertyMappingModel.fromJSON(this.modelData);
|
||||
return model.validate();
|
||||
}
|
||||
|
||||
private handleSave(): void {
|
||||
const model = PropertyMappingModel.fromJSON(this.modelData);
|
||||
if (!model.validate().res) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.save(model.toJSON());
|
||||
this.unsavedChanges = false;
|
||||
this.render();
|
||||
}
|
||||
|
||||
private onModelUpdate(): void {
|
||||
this.unsavedChanges = true;
|
||||
this.render();
|
||||
}
|
||||
|
||||
private render(): void {
|
||||
this.containerEl.empty();
|
||||
|
||||
const validationResult = this.validationResult;
|
||||
const rootEl = this.containerEl.createDiv('media-db-plugin-property-mappings-model-container');
|
||||
const headerEl = rootEl.createDiv('media-db-plugin-property-mappings-model-header');
|
||||
headerEl.createDiv({ cls: 'setting-item-name', text: capitalizeFirstLetter(this.modelData.type) });
|
||||
|
||||
const actionsEl = headerEl.createDiv('media-db-plugin-property-mappings-model-actions');
|
||||
if (this.unsavedChanges) {
|
||||
actionsEl.createDiv({ cls: 'media-db-plugin-property-mapping-unsaved-changes', text: 'Unsaved changes' });
|
||||
}
|
||||
|
||||
const saveButtonEl = actionsEl.createEl('button', {
|
||||
cls: `media-db-plugin-property-mappings-save-button ${validationResult.res ? 'mod-cta' : 'mod-muted'}`,
|
||||
text: 'Save',
|
||||
});
|
||||
saveButtonEl.addEventListener('click', () => this.handleSave());
|
||||
|
||||
if (!validationResult.res) {
|
||||
rootEl.createDiv({
|
||||
cls: 'media-db-plugin-property-mapping-validation',
|
||||
text: validationResult.err?.message ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
const tableContainerEl = rootEl.createDiv('media-db-plugin-property-mappings-table-container');
|
||||
const tableEl = tableContainerEl.createEl('table', 'media-db-plugin-property-mappings-table');
|
||||
const tableHeadEl = tableEl.createEl('thead');
|
||||
const headerRowEl = tableHeadEl.createEl('tr');
|
||||
headerRowEl.createEl('th', { cls: 'col-property', text: 'Property' });
|
||||
headerRowEl.createEl('th', { cls: 'col-mapping', text: 'Mapping' });
|
||||
headerRowEl.createEl('th', { cls: 'col-new-name', text: 'New name' });
|
||||
headerRowEl.createEl('th', { cls: 'col-wikilink', text: 'Wikilink' });
|
||||
|
||||
const tableBodyEl = tableEl.createEl('tbody');
|
||||
for (const [index, property] of this.modelData.properties.entries()) {
|
||||
const rowEl = tableBodyEl.createEl('tr');
|
||||
const propertyCellEl = rowEl.createEl('td', 'col-property');
|
||||
propertyCellEl.createEl('code', { text: property.property });
|
||||
|
||||
if (property.locked) {
|
||||
const lockedCellEl = rowEl.createEl('td', 'col-locked');
|
||||
lockedCellEl.colSpan = 3;
|
||||
lockedCellEl.createDiv({
|
||||
cls: 'media-db-plugin-property-binding-text',
|
||||
text: 'property cannot be remapped',
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const mappingCellEl = rowEl.createEl('td', 'col-mapping');
|
||||
const selectEl = mappingCellEl.createEl('select', 'dropdown');
|
||||
for (const remappingOption of propertyMappingOptions) {
|
||||
selectEl.createEl('option', {
|
||||
attr: { value: remappingOption },
|
||||
text: remappingOption,
|
||||
});
|
||||
}
|
||||
selectEl.value = propertyMappingOptions.includes(property.mapping) ? property.mapping : PropertyMappingOption.Default;
|
||||
selectEl.addEventListener('change', event => {
|
||||
const targetEl = event.currentTarget as HTMLSelectElement;
|
||||
this.modelData.properties[index].mapping = targetEl.value as PropertyMappingOption;
|
||||
this.modelData.properties[index].newProperty = '';
|
||||
this.onModelUpdate();
|
||||
});
|
||||
|
||||
const newNameCellEl = rowEl.createEl('td', 'col-new-name');
|
||||
if (property.mapping === PropertyMappingOption.Map) {
|
||||
const mappingToEl = newNameCellEl.createDiv('media-db-plugin-property-mapping-to');
|
||||
const iconWrapperEl = mappingToEl.createDiv('icon-wrapper');
|
||||
const iconEl = iconWrapperEl.createDiv('icon');
|
||||
setIcon(iconEl, 'arrow-right');
|
||||
|
||||
const inputEl = mappingToEl.createEl('input', {
|
||||
cls: 'media-db-plugin-property-mapping-input',
|
||||
type: 'text',
|
||||
});
|
||||
inputEl.spellcheck = false;
|
||||
inputEl.value = property.newProperty;
|
||||
inputEl.addEventListener('input', event => {
|
||||
const targetEl = event.currentTarget as HTMLInputElement;
|
||||
this.modelData.properties[index].newProperty = targetEl.value;
|
||||
this.onModelUpdate();
|
||||
});
|
||||
} else {
|
||||
newNameCellEl.createSpan({
|
||||
cls: 'media-db-plugin-property-mapping-to-disabled',
|
||||
text: 'N/A',
|
||||
});
|
||||
}
|
||||
|
||||
const wikilinkCellEl = rowEl.createEl('td', 'col-wikilink');
|
||||
const wikilinkLabelEl = wikilinkCellEl.createEl('label', {
|
||||
cls: 'media-db-plugin-property-mapping-wikilink-label',
|
||||
attr: { title: 'Convert value to wikilink ([[value]])' },
|
||||
});
|
||||
const wikilinkInputEl = wikilinkLabelEl.createEl('input', { type: 'checkbox' });
|
||||
wikilinkInputEl.checked = property.wikilink ?? false;
|
||||
wikilinkInputEl.addEventListener('change', event => {
|
||||
const targetEl = event.currentTarget as HTMLInputElement;
|
||||
this.modelData.properties[index].wikilink = targetEl.checked;
|
||||
this.onModelUpdate();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
import { createSignal, createMemo, For, Show } from 'solid-js';
|
||||
import { createStore } from 'solid-js/store';
|
||||
import { PropertyMappingModel, PropertyMappingOption, propertyMappingOptions, type PropertyMappingModelData } from './PropertyMapping';
|
||||
import { capitalizeFirstLetter } from '../utils/Utils';
|
||||
import Icon from './Icon';
|
||||
|
||||
interface PropertyMappingModelComponentProps {
|
||||
model: PropertyMappingModelData;
|
||||
save: (model: PropertyMappingModelData) => void;
|
||||
}
|
||||
|
||||
export default function PropertyMappingModelComponent(props: PropertyMappingModelComponentProps) {
|
||||
const [unsavedChanges, setUnsavedChanges] = createSignal(false);
|
||||
|
||||
// Create a store from the model's plain data
|
||||
const [modelData, setModelData] = createStore(props.model);
|
||||
|
||||
// Derive the validation result reactively
|
||||
const validationResult = createMemo(() => {
|
||||
const model = PropertyMappingModel.fromJSON(modelData);
|
||||
return model.validate();
|
||||
});
|
||||
|
||||
const onModelUpdate = () => {
|
||||
setUnsavedChanges(true);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
const model = PropertyMappingModel.fromJSON(modelData);
|
||||
if (model.validate().res) {
|
||||
props.save(model);
|
||||
setUnsavedChanges(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="media-db-plugin-property-mappings-model-container">
|
||||
<div class="media-db-plugin-property-mappings-model-header">
|
||||
<div class="setting-item-name">{capitalizeFirstLetter(modelData.type)}</div>
|
||||
|
||||
<div class="media-db-plugin-property-mappings-model-actions">
|
||||
<Show when={unsavedChanges()}>
|
||||
<div class="media-db-plugin-property-mapping-unsaved-changes">Unsaved changes</div>
|
||||
</Show>
|
||||
|
||||
<button class={`media-db-plugin-property-mappings-save-button ${validationResult().res ? 'mod-cta' : 'mod-muted'}`} onClick={handleSave}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Show when={!validationResult().res}>
|
||||
<div class="media-db-plugin-property-mapping-validation">{validationResult().err?.message}</div>
|
||||
</Show>
|
||||
|
||||
<div class="media-db-plugin-property-mappings-table-container">
|
||||
<table class="media-db-plugin-property-mappings-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-property">Property</th>
|
||||
<th class="col-mapping">Mapping</th>
|
||||
<th class="col-new-name">New name</th>
|
||||
<th class="col-wikilink">Wikilink</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<For each={modelData.properties}>
|
||||
{(property, index) => (
|
||||
<tr>
|
||||
<td class="col-property">
|
||||
<code>{property.property}</code>
|
||||
</td>
|
||||
|
||||
<Show
|
||||
when={!property.locked}
|
||||
fallback={
|
||||
<td class="col-locked" colspan={3}>
|
||||
<div class="media-db-plugin-property-binding-text">property cannot be remapped</div>
|
||||
</td>
|
||||
}
|
||||
>
|
||||
<td class="col-mapping">
|
||||
<select
|
||||
class="dropdown"
|
||||
value={property.mapping}
|
||||
onChange={e => {
|
||||
setModelData('properties', index(), 'mapping', e.currentTarget.value as PropertyMappingOption);
|
||||
setModelData('properties', index(), 'newProperty', '');
|
||||
onModelUpdate();
|
||||
}}
|
||||
>
|
||||
<For each={propertyMappingOptions}>{remappingOption => <option value={remappingOption}>{remappingOption}</option>}</For>
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td class="col-new-name">
|
||||
<Show
|
||||
when={property.mapping === PropertyMappingOption.Map}
|
||||
fallback={<span class="media-db-plugin-property-mapping-to-disabled">N/A</span>}
|
||||
>
|
||||
<div class="media-db-plugin-property-mapping-to">
|
||||
<Icon iconName="arrow-right" />
|
||||
<input
|
||||
class="media-db-plugin-property-mapping-input"
|
||||
type="text"
|
||||
spellcheck={false}
|
||||
value={property.newProperty}
|
||||
onInput={e => {
|
||||
setModelData('properties', index(), 'newProperty', e.currentTarget.value);
|
||||
onModelUpdate();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
</td>
|
||||
|
||||
<td class="col-wikilink">
|
||||
<label class="media-db-plugin-property-mapping-wikilink-label" title="Convert value to wikilink ([[value]])">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={property.wikilink}
|
||||
onChange={e => {
|
||||
setModelData('properties', index(), 'wikilink', e.currentTarget.checked);
|
||||
onModelUpdate();
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</td>
|
||||
</Show>
|
||||
</tr>
|
||||
)}
|
||||
</For>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import { Component } from 'obsidian';
|
||||
import type { PropertyMappingModelData } from 'packages/obsidian/src/settings/PropertyMapping';
|
||||
import PropertyMappingModelComponent from 'packages/obsidian/src/settings/PropertyMappingModelComponent';
|
||||
|
||||
interface PropertyMappingModelsComponentProps {
|
||||
models?: PropertyMappingModelData[];
|
||||
save: (model: PropertyMappingModelData) => void;
|
||||
}
|
||||
|
||||
export default class PropertyMappingModelsComponent extends Component {
|
||||
private readonly containerEl: HTMLElement;
|
||||
private readonly models: PropertyMappingModelData[];
|
||||
private readonly save: (model: PropertyMappingModelData) => void;
|
||||
|
||||
constructor(containerEl: HTMLElement, props: PropertyMappingModelsComponentProps) {
|
||||
super();
|
||||
this.containerEl = containerEl;
|
||||
this.models = props.models ?? [];
|
||||
this.save = props.save;
|
||||
}
|
||||
|
||||
override onload(): void {
|
||||
this.containerEl.empty();
|
||||
const rootEl = this.containerEl.createDiv('setting-item media-db-plugin-property-mappings-models-container');
|
||||
|
||||
for (const model of this.models) {
|
||||
const modelContainerEl = rootEl.createDiv();
|
||||
this.addChild(
|
||||
new PropertyMappingModelComponent(modelContainerEl, {
|
||||
model,
|
||||
save: this.save,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
override onunload(): void {
|
||||
this.containerEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
import { For } from 'solid-js';
|
||||
import { type PropertyMappingModelData } from './PropertyMapping';
|
||||
import PropertyMappingModelComponent from './PropertyMappingModelComponent';
|
||||
|
||||
interface PropertyMappingModelsComponentProps {
|
||||
models?: PropertyMappingModelData[];
|
||||
save: (model: PropertyMappingModelData) => void;
|
||||
}
|
||||
|
||||
export default function PropertyMappingModelsComponent(props: PropertyMappingModelsComponentProps) {
|
||||
return (
|
||||
<div class="setting-item" style={{ display: 'flex', gap: '10px', 'flex-direction': 'column', 'align-items': 'stretch' }}>
|
||||
<For each={props.models || []}>{model => <PropertyMappingModelComponent model={model} save={props.save} />}</For>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@ import { FolderSuggest } from 'packages/obsidian/src/settings/suggesters/FolderS
|
|||
import { MediaType } from 'packages/obsidian/src/utils/MediaType';
|
||||
import { MEDIA_TYPES } from 'packages/obsidian/src/utils/MediaTypeManager';
|
||||
import { unCamelCase } from 'packages/obsidian/src/utils/Utils';
|
||||
import { render } from 'solid-js/web';
|
||||
|
||||
function createDateFormatDescription(preview: string): DocumentFragment {
|
||||
return createFragment(frag => {
|
||||
|
|
@ -425,14 +424,25 @@ export function getDefaultSettings(plugin: MediaDbPlugin): MediaDbPluginSettings
|
|||
// MARK: Settings Tab
|
||||
export class MediaDbSettingTab extends PluginSettingTab {
|
||||
plugin: MediaDbPlugin;
|
||||
private propertyMappingModelsComponent?: PropertyMappingModelsComponent;
|
||||
|
||||
constructor(app: App, plugin: MediaDbPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
override hide(): void {
|
||||
this.propertyMappingModelsComponent?.unload();
|
||||
this.propertyMappingModelsComponent = undefined;
|
||||
super.hide();
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
if (this.propertyMappingModelsComponent) {
|
||||
this.propertyMappingModelsComponent.unload();
|
||||
this.propertyMappingModelsComponent = undefined;
|
||||
}
|
||||
containerEl.empty();
|
||||
|
||||
const mediaTypeSettings = MEDIA_TYPES.map(mt => new MediaTypeMappedSettings(mt));
|
||||
|
|
@ -837,24 +847,21 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
mappingGroup.setHeading('Property mappings');
|
||||
mappingGroup.addSetting(setting => {
|
||||
setting.setName('Property mappings explanation').setDesc(createPropertyMappingsDescription());
|
||||
const propertyMappingsEl = setting.descEl.createDiv();
|
||||
this.propertyMappingModelsComponent = new PropertyMappingModelsComponent(propertyMappingsEl, {
|
||||
models: structuredClone(this.plugin.settings.propertyMappingModels),
|
||||
save: (model: PropertyMappingModelData): void => {
|
||||
// Update the matching model in settings (stored as plain data)
|
||||
const index = this.plugin.settings.propertyMappingModels.findIndex(m => m.type === model.type);
|
||||
if (index !== -1) {
|
||||
this.plugin.settings.propertyMappingModels[index] = model;
|
||||
}
|
||||
|
||||
render(
|
||||
() =>
|
||||
PropertyMappingModelsComponent({
|
||||
models: structuredClone(this.plugin.settings.propertyMappingModels),
|
||||
save: (model: PropertyMappingModelData): void => {
|
||||
// Update the matching model in settings (stored as plain data)
|
||||
const index = this.plugin.settings.propertyMappingModels.findIndex(m => m.type === model.type);
|
||||
if (index !== -1) {
|
||||
this.plugin.settings.propertyMappingModels[index] = model;
|
||||
}
|
||||
|
||||
new Notice(`MDB: Property mappings for ${model.type} saved successfully.`);
|
||||
void this.plugin.saveSettings();
|
||||
},
|
||||
}),
|
||||
setting.descEl,
|
||||
);
|
||||
new Notice(`MDB: Property mappings for ${model.type} saved successfully.`);
|
||||
void this.plugin.saveSettings();
|
||||
},
|
||||
});
|
||||
this.propertyMappingModelsComponent.load();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue