Skip to content

Commit 9767770

Browse files
author
unxok
committed
added stars widget
1 parent f68d8fb commit 9767770

File tree

13 files changed

+172
-119
lines changed

13 files changed

+172
-119
lines changed

README.md

Lines changed: 3 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,5 @@
1-
# Obsidian Sample Plugin
1+
# Better Properties
22

3-
This is a sample plugin for Obsidian (https://obsidian.md).
3+
Adds additional property types and configuration options to Obsidian's core **Properties View** plugin!
44

5-
This project uses TypeScript to provide type checking and documentation.
6-
The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definition format, which contains TSDoc comments describing what it does.
7-
8-
**Note:** The Obsidian API is still in early alpha and is subject to change at any time!
9-
10-
This sample plugin demonstrates some of the basic functionality the plugin API can do.
11-
- Adds a ribbon icon, which shows a Notice when clicked.
12-
- Adds a command "Open Sample Modal" which opens a Modal.
13-
- Adds a plugin setting tab to the settings page.
14-
- Registers a global click event and output 'click' to the console.
15-
- Registers a global interval which logs 'setInterval' to the console.
16-
17-
## First time developing plugins?
18-
19-
Quick starting guide for new plugin devs:
20-
21-
- Check if [someone already developed a plugin for what you want](https://obsidian.md/plugins)! There might be an existing plugin similar enough that you can partner up with.
22-
- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it).
23-
- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder.
24-
- Install NodeJS, then run `npm i` in the command line under your repo folder.
25-
- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`.
26-
- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`.
27-
- Reload Obsidian to load the new version of your plugin.
28-
- Enable plugin in settings window.
29-
- For updates to the Obsidian API run `npm update` in the command line under your repo folder.
30-
31-
## Releasing new releases
32-
33-
- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release.
34-
- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible.
35-
- Create new GitHub release using your new version number as the "Tag version". Use the exact version number, don't include a prefix `v`. See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases
36-
- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments. Note: The manifest.json file must be in two places, first the root path of your repository and also in the release.
37-
- Publish the release.
38-
39-
> You can simplify the version bump process by running `npm version patch`, `npm version minor` or `npm version major` after updating `minAppVersion` manually in `manifest.json`.
40-
> The command will bump version in `manifest.json` and `package.json`, and add the entry for the new version to `versions.json`
41-
42-
## Adding your plugin to the community plugin list
43-
44-
- Check the [plugin guidelines](https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines).
45-
- Publish an initial version.
46-
- Make sure you have a `README.md` file in the root of your repo.
47-
- Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin.
48-
49-
## How to use
50-
51-
- Clone this repo.
52-
- Make sure your NodeJS is at least v16 (`node --version`).
53-
- `npm i` or `yarn` to install dependencies.
54-
- `npm run dev` to start compilation in watch mode.
55-
56-
## Manually installing the plugin
57-
58-
- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`.
59-
60-
## Improve code quality with eslint (optional)
61-
- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code.
62-
- To use eslint with this project, make sure to install eslint from terminal:
63-
- `npm install -g eslint`
64-
- To use eslint to analyze this project use this command:
65-
- `eslint main.ts`
66-
- eslint will then create a report with suggestions for code improvement by file and line number.
67-
- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder:
68-
- `eslint .\src\`
69-
70-
## Funding URL
71-
72-
You can include funding URLs where people who use your plugin can financially support it.
73-
74-
The simple way is to set the `fundingUrl` field to your link in your `manifest.json` file:
75-
76-
```json
77-
{
78-
"fundingUrl": "https://buymeacoffee.com"
79-
}
80-
```
81-
82-
If you have multiple URLs, you can also do:
83-
84-
```json
85-
{
86-
"fundingUrl": {
87-
"Buy Me a Coffee": "https://buymeacoffee.com",
88-
"GitHub Sponsor": "https://github.com/sponsors",
89-
"Patreon": "https://www.patreon.com/"
90-
}
91-
}
92-
```
93-
94-
## API Documentation
95-
96-
See https://github.com/obsidianmd/obsidian-api
5+
![preview](./preview.png)

manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"id": "better-properties",
33
"name": "Better Properties",
4-
"version": "1.0.0",
5-
"minAppVersion": "0.15.0",
4+
"version": "0.0.1",
5+
"minAppVersion": "1.5.0",
66
"description": "Upgrades Obsidian properties with new types, features, and more!",
77
"author": "Unxok",
88
"authorUrl": "https://github.com/unxok",

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
22
"name": "obsidian-better-properties",
3-
"version": "1.0.0",
3+
"version": "0.0.1",
44
"description": "Upgrades Obsidian properties with new types, features, and more!",
55
"main": "main.js",
66
"scripts": {
77
"dev": "vite build --watch --mode=development",
88
"build": "vite build",
9-
"version": "node version-bump.mjs && git add manifest.json versions.json"
9+
"version": "node version-bump.mjs && git add manifest.json versions.json",
10+
"release": "git tag -a %npm_config_tag% -m '%npm_config_tag%' && git push origin %npm_config_tag%"
1011
},
1112
"keywords": [],
1213
"author": "",

preview.png

16.3 KB
Loading

src/libs/PropertySettings/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ export type PropertySettings = {
4747
color: {};
4848
markdown: {};
4949
email: {};
50+
stars: {
51+
customIcon: string;
52+
max: number;
53+
};
5054
};
5155

5256
// can't think of a way to have this typed properly but at least this avoids hard coding the keys somewhat
@@ -94,4 +98,8 @@ export const defaultPropertySettings: PropertySettings = {
9498
color: {},
9599
markdown: {},
96100
email: {},
101+
stars: {
102+
customIcon: "star",
103+
max: 5,
104+
},
97105
};

src/libs/utils/augmentMedataMenu/addSettings/index.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
defaultPropertySettings,
1818
PropertySettings,
1919
} from "@/libs/PropertySettings";
20+
import { createStarsSettings } from "@/typeWidgets/Stars";
2021

2122
export const addSettings = ({ menu, plugin, key }: MetadataAddItemProps) => {
2223
menu.addItem((item) =>
@@ -57,7 +58,6 @@ class SettingsModal extends Modal {
5758
// @ts-ignore TODO IDK why typescript doesn't like this
5859
form[key] = { ...defaultValue, ...form[key] };
5960
});
60-
console.log("form after parse: ", form);
6161
this.form = form;
6262
}
6363

@@ -133,9 +133,7 @@ class SettingsModal extends Modal {
133133
});
134134

135135
switch (
136-
typeKey.slice(
137-
typeWidgetPrefix.length
138-
) as keyof typeof typeKeySuffixes
136+
typeKey.slice(typeWidgetPrefix.length) as keyof PropertySettings
139137
) {
140138
case "slider":
141139
return createSliderSettings(
@@ -163,6 +161,13 @@ class SettingsModal extends Modal {
163161
(key, value) => this.updateForm("button", key, value),
164162
this.plugin
165163
);
164+
case "stars":
165+
return createStarsSettings(
166+
contentEl,
167+
this.form["stars"],
168+
(key, value) => this.updateForm("stars", key, value),
169+
this.plugin
170+
);
166171
default:
167172
new Setting(contentEl)
168173
.setName("Non customizable type")
@@ -175,7 +180,6 @@ class SettingsModal extends Modal {
175180
async onClose(): Promise<void> {
176181
const { plugin, property, form } = this;
177182
const key = property.toLowerCase();
178-
console.log("about to save form: ", form);
179183
await plugin.updateSettings((prev) => ({
180184
...prev,
181185
propertySettings: { ...prev.propertySettings, [key]: form },

src/libs/utils/pure/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,16 @@ export const getButtonStyledClass = (
2828
if (style === "warning") return "mod-warning";
2929
return "";
3030
};
31+
32+
export const clampNumber = (
33+
num: number,
34+
min: number,
35+
max: number,
36+
nonInclusive?: boolean
37+
) => {
38+
const underMin = nonInclusive ? num < min : num <= min;
39+
if (underMin) return min;
40+
const overMax = nonInclusive ? num > max : num >= max;
41+
if (overMax) return max;
42+
return num;
43+
};

src/main.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,6 @@ export default class BetterProperties extends Plugin {
194194
}
195195
}
196196

197-
type PatchedMetadataEditor = MetadataEditor & {
198-
toggleHiddenButton: HTMLDivElement;
199-
showHiddenProperties: boolean;
200-
toggleHiddenProperties(): void;
201-
};
202-
203197
const patchMenu = (plugin: BetterProperties) => {
204198
const removePatch = around(Menu.prototype, {
205199
showAtMouseEvent(old) {
@@ -236,6 +230,12 @@ const patchMenu = (plugin: BetterProperties) => {
236230
plugin.register(removePatch);
237231
};
238232

233+
export type PatchedMetadataEditor = MetadataEditor & {
234+
toggleHiddenButton: HTMLDivElement;
235+
showHiddenProperties: boolean;
236+
toggleHiddenProperties(): void;
237+
};
238+
239239
const patchMetdataEditor = (plugin: BetterProperties) => {
240240
const view = plugin.app.viewRegistry.viewByType["markdown"]({
241241
containerEl: createDiv(),

src/typeWidgets/Button/index.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,10 @@ export const ButtonWidget: CustomTypeWidget = {
3939

4040
btn.buttonEl.setAttribute("style", "margin-top: 0px;");
4141

42-
btn.buttonEl.classList.add(getButtonStyledClass(style));
43-
44-
// if (style === "accent") btn.setCta();
45-
// if (style === "warning") btn.setWarning();
46-
// if (style === "destructive")
47-
// btn.buttonEl.classList.add("mod-destructive");
48-
// if (style === "muted") btn.buttonEl.classList.add("mod-muted");
49-
// if (style === "ghost") btn.buttonEl.classList.add("clickable-icon");
42+
const className = getButtonStyledClass(style);
43+
if (className) {
44+
btn.buttonEl.classList.add(className);
45+
}
5046
if (bgColor) {
5147
btn.buttonEl.style.backgroundColor = bgColor;
5248
}
@@ -179,7 +175,11 @@ export const createButtonSettings = (
179175
.addButton((cmp) =>
180176
cmp
181177
.setButtonText("preview")
182-
.setClass(getButtonStyledClass(style))
178+
.then((cmp) => {
179+
const cn = getButtonStyledClass(style);
180+
if (!cn) return;
181+
cmp.setClass(cn);
182+
})
183183
.then((cmp) => (stylePreviewBtn = cmp))
184184
);
185185

src/typeWidgets/Stars/index.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { ButtonComponent, DropdownComponent, setIcon, Setting } from "obsidian";
2+
import { CustomTypeWidget } from "..";
3+
import { clampNumber, getButtonStyledClass } from "@/libs/utils/pure";
4+
import { IconSuggest } from "@/classes/IconSuggest";
5+
import { TextColorComponent } from "@/classes/TextColorComponent";
6+
import { createSection } from "@/libs/utils/setting";
7+
import BetterProperties from "@/main";
8+
import {
9+
defaultPropertySettings,
10+
PropertySettings,
11+
} from "@/libs/PropertySettings";
12+
13+
export const StarsWidget: CustomTypeWidget = {
14+
type: "stars",
15+
icon: "star",
16+
default: () => 0,
17+
name: () => "Stars",
18+
validate: (v) => !Number.isNaN(Number(v)),
19+
render: (plugin, el, data, ctx) => {
20+
const { customIcon, max } = plugin.settings.propertySettings[
21+
data.key.toLowerCase()
22+
]?.["stars"] ?? {
23+
...defaultPropertySettings["stars"],
24+
};
25+
26+
const container = el.createDiv({
27+
cls: "metadata-input-longtext",
28+
attr: {
29+
"data-count": data.value?.toString() ?? "1",
30+
},
31+
});
32+
33+
const min = 0;
34+
35+
const count = clampNumber(Number(data.value), min, max);
36+
const starEls: HTMLElement[] = [];
37+
38+
for (let i = 1; i < max + 1; i++) {
39+
const span = container.createSpan({
40+
cls: "clickable-icon better-properties-inline-flex",
41+
attr: {
42+
"aria-label": i.toString(),
43+
},
44+
});
45+
starEls.push(span);
46+
setIcon(span, customIcon);
47+
48+
if (i <= count) {
49+
span.classList.add("better-properties-svg-fill");
50+
}
51+
52+
span.addEventListener("click", () => {
53+
const existingCount = clampNumber(
54+
Number(container.getAttribute("data-count")),
55+
min,
56+
max
57+
);
58+
const newCount = existingCount !== i ? i : i - 1;
59+
container.setAttribute("data-count", newCount.toString());
60+
starEls.forEach((el, k) => {
61+
if (k + 1 <= newCount) {
62+
return el.classList.add("better-properties-svg-fill");
63+
}
64+
el.classList.remove("better-properties-svg-fill");
65+
});
66+
ctx.onChange(newCount);
67+
});
68+
}
69+
},
70+
};
71+
72+
export const createStarsSettings = (
73+
el: HTMLElement,
74+
form: PropertySettings["stars"],
75+
updateForm: <T extends keyof PropertySettings["stars"]>(
76+
key: T,
77+
value: PropertySettings["stars"][T]
78+
) => void,
79+
plugin: BetterProperties
80+
// defaultOpen: boolean
81+
) => {
82+
const { customIcon, max } = form;
83+
84+
const { content } = createSection(el, "Button", true);
85+
86+
new Setting(content)
87+
.setName("Override star icon")
88+
.setDesc("Set a custom icon to show in place of the default stars.")
89+
.addText((cmp) =>
90+
cmp
91+
.setValue(customIcon)
92+
.onChange((v) => updateForm("customIcon", v))
93+
.then((cmp) => new IconSuggest(plugin.app, cmp))
94+
);
95+
96+
new Setting(content)
97+
.setName("Count")
98+
.setDesc("How many stars to display.")
99+
.addText((cmp) =>
100+
cmp
101+
.setValue(max.toString())
102+
.onChange((v) => updateForm("max", Number(v)))
103+
.then((cmp) => {
104+
cmp.inputEl.setAttribute("type", "number");
105+
})
106+
);
107+
};

0 commit comments

Comments
 (0)