Skip to content

Commit

Permalink
Add collapsible sections
Browse files Browse the repository at this point in the history
  • Loading branch information
mgmeyers committed Apr 12, 2021
1 parent 3d5bccb commit 7c2b5ae
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 98 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ All settings definitions must have these parameters:

## `heading`

`heading`s can be used to organize and group settings, but do not add any functionality. Along with the required attributes, `heading`s must contain a `level` attribute:
`heading`s can be used to organize and group settings into collapsable nested sections. Along with the required attributes, `heading`s must contain a `level` attribute between `1` and `6`, and can optionally contain a `collapsed` attribute:

```css
/* @settings
Expand All @@ -71,6 +71,7 @@ settings:
title: My Heading
type: heading
level: 2
collapsed: true
*/
```
Expand Down
37 changes: 17 additions & 20 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { CSSSettingsManager } from "./SettingsManager";
import {
CleanupFunction,
createHeading,
createSetting,
createSettings,
CSSSetting,
ParsedCSSSettings,
} from "./settingHandlers";
import { parse } from "yaml";

import "@simonwep/pickr/dist/themes/nano.min.css";
import "./pickerOverrides.css";
import "./settings.css";

const settingRegExp = /\/\*\s*@settings[\r\n]+?([\s\S]+?)\*\//g;

Expand Down Expand Up @@ -83,6 +85,7 @@ class CSSSettingsTab extends PluginSettingTab {
this.cleanupFns.forEach((fn) => fn && fn());
}

// TODO: only generate on display()
generate(settings: ParsedCSSSettings[]) {
let { containerEl, plugin } = this;

Expand All @@ -94,28 +97,22 @@ class CSSSettingsTab extends PluginSettingTab {
const cleanupFns: CleanupFunction[] = [];

settings.forEach((s) => {
createHeading({
config: {
id: s.id,
type: "heading",
title: s.name,
level: 1,
},
const options: CSSSetting[] = [{
id: s.id,
type: "heading",
title: s.name,
level: 0,
collapsed: true,
}, ...s.settings]

const cleanup = createSettings({
containerEl,
sectionId: s.id,
settings: options,
settingsManager: plugin.settingsManager,
});

s.settings.forEach((setting) => {
const cleanup = createSetting({
containerEl,
sectionId: s.id,
setting,
settingsManager: plugin.settingsManager,
});

if (typeof cleanup === "function") {
cleanupFns.push(cleanup);
}
});
if (cleanup.length) cleanupFns.push(...cleanup)
});

this.cleanupFns = cleanupFns;
Expand Down
235 changes: 159 additions & 76 deletions src/settingHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
TextComponent,
debounce,
ButtonComponent,
setIcon,
} from "obsidian";
import { CSSSettingsManager } from "./SettingsManager";
import Pickr from "@simonwep/pickr";
Expand All @@ -31,11 +32,11 @@ function createDescription(description: string | undefined, def: string) {
small.appendChild(createEl("strong", { text: "Default: " }));
small.appendChild(document.createTextNode(def));

const p = createEl("p");
const div = createEl("div");

p.appendChild(small);
div.appendChild(small);

fragment.appendChild(p);
fragment.appendChild(div);
}

return fragment;
Expand All @@ -51,26 +52,33 @@ interface Meta {
}

export interface Heading extends Meta {
level: 1 | 2 | 3 | 4 | 5 | 6;
level: 0 | 1 | 2 | 3 | 4 | 5 | 6;
collapsed?: boolean;
}

export function createHeading(opts: {
config: Heading;
containerEl: HTMLElement;
}) {
const level = `h${opts.config.level}` as
| "h1"
| "h2"
| "h3"
| "h4"
| "h5"
| "h6";

opts.containerEl.createEl(level, { text: opts.config.title });

if (opts.config.description) {
opts.containerEl.createEl("p", { text: opts.config.description });
}
new Setting(opts.containerEl)
.setHeading()
.setClass('style-settings-heading')
.setName(opts.config.title)
.setDesc(opts.config.description ? opts.config.description : "")
.then((setting) => {
if (opts.config.collapsed) setting.settingEl.addClass('is-collapsed');
setting.settingEl.dataset.level = opts.config.level.toString();

const iconContainer = createSpan({ cls: 'style-settings-collapse-indicator' })

setIcon(iconContainer, 'right-triangle')

setting.nameEl.prepend(iconContainer)

setting.settingEl.addEventListener('click', (e) => {
setting.settingEl.toggleClass('is-collapsed', !setting.settingEl.hasClass('is-collapsed'));
})
});
}

export interface ClassToggle extends Meta {}
Expand Down Expand Up @@ -594,68 +602,143 @@ export interface ParsedCSSSettings {
settings: Array<CSSSetting>;
}

export function createSetting(opts: {
export function createSettings(opts: {
containerEl: HTMLElement;
sectionId: string;
setting: CSSSetting;
settings: CSSSetting[];
settingsManager: CSSSettingsManager;
}): CleanupFunction {
const { containerEl, sectionId, setting, settingsManager } = opts;
}): CleanupFunction[] {
const { containerEl, sectionId, settings, settingsManager } = opts;

switch (setting.type) {
case "heading":
return createHeading({
config: setting as Heading,
containerEl,
});
case "class-toggle":
return createClassToggle({
sectionId,
config: setting as ClassToggle,
containerEl,
settingsManager,
});
case "variable-text":
return createVariableText({
sectionId,
config: setting as VariableText,
containerEl,
settingsManager,
});
case "variable-number":
return createVariableNumber({
sectionId,
config: setting as VariableNumber,
containerEl,
settingsManager,
});
case "variable-number-slider":
return createVariableNumberSlider({
sectionId,
config: setting as VariableNumberSlider,
containerEl,
settingsManager,
});
case "variable-select":
return createVariableSelect({
sectionId,
config: setting as VariableSelect,
containerEl,
settingsManager,
});
case "variable-color":
return createVariableColor({
sectionId,
config: setting as VariableColor,
containerEl,
settingsManager,
});
case "variable-themed-color":
return createVariableThemedColor({
sectionId,
config: setting as VariableThemedColor,
containerEl,
settingsManager,
});
const containerStack: HTMLElement[] = [containerEl];
const cleanup: CleanupFunction[] = [];

let containerLevel = 0;

function getTargetContainer(stack: HTMLElement[]) {
if (!stack.length) return containerEl;
return stack[stack.length - 1];
}

settings.forEach((setting) => {
switch (setting.type) {
case "heading": {
const config = setting as Heading;

let targetContainer = getTargetContainer(containerStack);

console.log(containerLevel, containerStack)

if (config.level > containerLevel) {
createHeading({
config,
containerEl: targetContainer,
});

targetContainer.createDiv(
{ cls: "style-settings-container" },
(container) => {
containerStack.push(container);
}
);
} else if (config.level === containerLevel) {
containerStack.pop();
targetContainer = getTargetContainer(containerStack);

createHeading({
config,
containerEl: targetContainer,
});

targetContainer.createDiv(
{ cls: "style-settings-container" },
(container) => {
containerStack.push(container);
}
);
} else {
containerStack.pop();
targetContainer = getTargetContainer(containerStack);

createHeading({
config,
containerEl: targetContainer,
});
}

containerLevel = config.level;

break;
}
case "class-toggle": {
createClassToggle({
sectionId,
config: setting as ClassToggle,
containerEl: getTargetContainer(containerStack),
settingsManager,
});
break;
}
case "variable-text": {
createVariableText({
sectionId,
config: setting as VariableText,
containerEl: getTargetContainer(containerStack),
settingsManager,
});
break;
}
case "variable-number": {
createVariableNumber({
sectionId,
config: setting as VariableNumber,
containerEl: getTargetContainer(containerStack),
settingsManager,
});
break;
}
case "variable-number-slider": {
createVariableNumberSlider({
sectionId,
config: setting as VariableNumberSlider,
containerEl: getTargetContainer(containerStack),
settingsManager,
});
break;
}
case "variable-select": {
createVariableSelect({
sectionId,
config: setting as VariableSelect,
containerEl: getTargetContainer(containerStack),
settingsManager,
});
break;
}
case "variable-color": {
cleanup.push(
createVariableColor({
sectionId,
config: setting as VariableColor,
containerEl: getTargetContainer(containerStack),
settingsManager,
})
);
break;
}
case "variable-themed-color": {
cleanup.push(
createVariableThemedColor({
sectionId,
config: setting as VariableThemedColor,
containerEl: getTargetContainer(containerStack),
settingsManager,
})
);
break;
}
}
});

return cleanup;
}
Loading

0 comments on commit 7c2b5ae

Please sign in to comment.