browser-extension-settings 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/common.ts CHANGED
@@ -1,3 +1,3 @@
1
- export const besVersion = 14
1
+ export const besVersion = 15
2
2
  export const openButton = `<svg viewBox="0 0 60.2601318359375 84.8134765625" version="1.1" xmlns="http://www.w3.org/2000/svg" class=" glyph-box" style="height: 9.62969px; width: 6.84191px;"><g transform="matrix(1 0 0 1 -6.194965820312518 77.63671875)"><path d="M66.4551-35.2539C66.4551-36.4746 65.9668-37.5977 65.0391-38.4766L26.3672-76.3672C25.4883-77.1973 24.4141-77.6367 23.1445-77.6367C20.6543-77.6367 18.7012-75.7324 18.7012-73.1934C18.7012-71.9727 19.1895-70.8496 19.9707-70.0195L55.5176-35.2539L19.9707-0.488281C19.1895 0.341797 18.7012 1.41602 18.7012 2.68555C18.7012 5.22461 20.6543 7.12891 23.1445 7.12891C24.4141 7.12891 25.4883 6.68945 26.3672 5.81055L65.0391-32.0312C65.9668-32.959 66.4551-34.0332 66.4551-35.2539Z"></path></g></svg>`
3
3
  export const openInNewTabButton = `<svg viewBox="0 0 72.127685546875 72.2177734375" version="1.1" xmlns="http://www.w3.org/2000/svg" class=" glyph-box" style="height: 8.19958px; width: 8.18935px;"><g transform="matrix(1 0 0 1 -12.451127929687573 71.3388671875)"><path d="M84.5703-17.334L84.5215-66.4551C84.5215-69.2383 82.7148-71.1914 79.7852-71.1914L30.6641-71.1914C27.9297-71.1914 26.0742-69.0918 26.0742-66.748C26.0742-64.4043 28.1738-62.4023 30.4688-62.4023L47.4609-62.4023L71.2891-63.1836L62.207-55.2246L13.8184-6.73828C12.9395-5.85938 12.4512-4.73633 12.4512-3.66211C12.4512-1.31836 14.5508 0.878906 16.9922 0.878906C18.1152 0.878906 19.1895 0.488281 20.0684-0.439453L68.5547-48.877L76.6113-58.0078L75.7324-35.2051L75.7324-17.1387C75.7324-14.8438 77.7344-12.6953 80.127-12.6953C82.4707-12.6953 84.5703-14.6973 84.5703-17.334Z"></path></g></svg>`
package/lib/index.ts CHANGED
@@ -1 +1,7 @@
1
- export { initSettings, showSettings, getSettingsValue } from "./settings"
1
+ export {
2
+ initSettings,
3
+ showSettings,
4
+ getSettingsValue,
5
+ saveSattingsValues,
6
+ resetSattingsValues,
7
+ } from "./settings"
package/lib/settings.ts CHANGED
@@ -33,10 +33,14 @@ type SettingsOptions = {
33
33
  footer?: string
34
34
  settingsTable?: SettingsTable
35
35
  onValueChange?: () => void
36
+ onViewUpdate?: (settingsMainView: HTMLElement) => void
36
37
  relatedExtensions?: RelatedExtension[]
37
38
  }
38
39
 
39
- type SettingsTable = Record<string, SettingsSwitchItem | SettingsInputItem>
40
+ type SettingsTable = Record<
41
+ string,
42
+ SettingsSwitchItem | SettingsInputItem | SettingsActionItem | SettingsTipItem
43
+ >
40
44
 
41
45
  type SettingsSwitchItem = {
42
46
  title: string
@@ -53,6 +57,22 @@ type SettingsInputItem = {
53
57
  group?: number
54
58
  }
55
59
 
60
+ type SettingsActionItem = {
61
+ title: string
62
+ type: string
63
+ onclick?: () => void
64
+ group?: number
65
+ defaultValue?: any
66
+ }
67
+
68
+ type SettingsTipItem = {
69
+ title: string
70
+ type: string
71
+ tipContent: string
72
+ group?: number
73
+ defaultValue?: any
74
+ }
75
+
56
76
  type RelatedExtension = {
57
77
  id: string
58
78
  title: string
@@ -95,6 +115,29 @@ async function saveSattingsValue(key: string, value: any) {
95
115
  await setValue(storageKey, settings)
96
116
  }
97
117
 
118
+ export async function resetSattingsValues() {
119
+ await setValue(storageKey, {})
120
+ }
121
+
122
+ export async function saveSattingsValues(
123
+ values: Record<string, boolean | string | undefined>
124
+ ) {
125
+ const settings = await getSettings()
126
+
127
+ for (const key in values) {
128
+ if (Object.hasOwn(values, key)) {
129
+ const value = values[key]
130
+
131
+ settings[key] =
132
+ settingsTable[key] && settingsTable[key].defaultValue === value
133
+ ? undefined
134
+ : value
135
+ }
136
+ }
137
+
138
+ await setValue(storageKey, settings)
139
+ }
140
+
98
141
  export function getSettingsValue(key: string): boolean | string | undefined {
99
142
  return Object.hasOwn(settings, key)
100
143
  ? (settings[key] as boolean | string | undefined)
@@ -160,7 +203,10 @@ async function updateOptions() {
160
203
  const textArea = $(
161
204
  `#${settingsElementId} .option_groups textarea[data-key="${key}"]`
162
205
  ) as HTMLTextAreaElement
163
- textArea.value = getSettingsValue(key) as string
206
+ if (textArea) {
207
+ textArea.value = getSettingsValue(key) as string
208
+ }
209
+
164
210
  break
165
211
  }
166
212
 
@@ -171,14 +217,9 @@ async function updateOptions() {
171
217
  }
172
218
  }
173
219
 
174
- const host = location.host
175
- const group2 = $(`#${settingsElementId} .option_groups:nth-of-type(2)`)
176
- if (group2) {
177
- group2.style.display = getSettingsValue(
178
- `enableCustomRulesForCurrentSite_${host}`
179
- )
180
- ? "block"
181
- : "none"
220
+ if (typeof settingsOptions.onViewUpdate === "function") {
221
+ const settingsMain = createSettingsElement()
222
+ settingsOptions.onViewUpdate(settingsMain)
182
223
  }
183
224
  }
184
225
 
@@ -274,67 +315,77 @@ function createSettingsElement() {
274
315
  const group = item.group || 1
275
316
  const optionGroup = getOptionGroup(group)
276
317
  // console.log(key, item, type, group)
277
- if (type === "switch") {
278
- const switchOption = createSwitchOption(item.title, {
279
- async onchange(event: Event) {
280
- if (event.target) {
281
- await saveSattingsValue(key, event.target.checked)
282
- }
283
- },
284
- })
285
-
286
- switchOption.dataset.key = key
287
-
288
- addElement(optionGroup, switchOption)
289
- } else if (type === "textarea") {
290
- let timeoutId: number | undefined
291
- addElement(optionGroup, "textarea", {
292
- "data-key": key,
293
- placeholder: (item as SettingsInputItem).placeholder || "",
294
- onkeyup(event: Event) {
295
- const textArea = event.target as HTMLTextAreaElement
296
- if (timeoutId) {
297
- clearTimeout(timeoutId)
298
- timeoutId = undefined
299
- }
300
-
301
- timeoutId = setTimeout(async () => {
302
- if (textArea) {
303
- await saveSattingsValue(key, textArea.value.trim())
318
+ switch (type) {
319
+ case "switch": {
320
+ const switchOption = createSwitchOption(item.title, {
321
+ async onchange(event: Event) {
322
+ const checkbox = event.target as HTMLInputElement
323
+ if (checkbox) {
324
+ await saveSattingsValue(key, checkbox.checked)
304
325
  }
305
- }, 100)
306
- },
307
- })
326
+ },
327
+ })
328
+
329
+ switchOption.dataset.key = key
330
+
331
+ addElement(optionGroup, switchOption)
332
+
333
+ break
334
+ }
335
+
336
+ case "textarea": {
337
+ let timeoutId: number | undefined
338
+ addElement(optionGroup, "textarea", {
339
+ "data-key": key,
340
+ placeholder: (item as SettingsInputItem).placeholder || "",
341
+ onkeyup(event: Event) {
342
+ const textArea = event.target as HTMLTextAreaElement
343
+ if (timeoutId) {
344
+ clearTimeout(timeoutId)
345
+ timeoutId = undefined
346
+ }
347
+
348
+ timeoutId = setTimeout(async () => {
349
+ if (textArea) {
350
+ await saveSattingsValue(key, textArea.value.trim())
351
+ }
352
+ }, 100)
353
+ },
354
+ })
355
+
356
+ break
357
+ }
358
+
359
+ case "action": {
360
+ addElement(optionGroup, "a", {
361
+ class: "action",
362
+ textContent: item.title,
363
+ onclick: (item as SettingsActionItem).onclick,
364
+ })
365
+
366
+ break
367
+ }
368
+
369
+ case "tip": {
370
+ const tip = addElement(optionGroup, "div", {
371
+ class: "bes_tip",
372
+ })
373
+ addElement(tip, "a", {
374
+ class: "bes_tip_anchor",
375
+ textContent: item.title,
376
+ })
377
+ const tipContent = addElement(tip, "div", {
378
+ class: "bes_tip_content",
379
+ // eslint-disable-next-line @typescript-eslint/naming-convention
380
+ innerHTML: (item as SettingsTipItem).tipContent,
381
+ })
382
+ break
383
+ }
384
+ // No default
308
385
  }
309
386
  }
310
387
  }
311
388
 
312
- const options2 = getOptionGroup(2)
313
-
314
- const tip = addElement(options2, "div", {
315
- class: "tip",
316
- })
317
- addElement(tip, "a", {
318
- class: "tip_anchor",
319
- textContent: "Examples",
320
- })
321
- const tipContent = addElement(tip, "div", {
322
- class: "tip_content",
323
- // eslint-disable-next-line @typescript-eslint/naming-convention
324
- innerHTML: `<p>Custom rules for internal URLs, matching URLs will be opened in new tabs</p>
325
- <p>
326
- - One line per url pattern<br>
327
- - All URLs contains '/posts' or '/users/'<br>
328
- <pre>/posts/
329
- /users/</pre>
330
-
331
- - Regex is supported<br>
332
- <pre>^/(posts|members)/d+</pre>
333
-
334
- - '*' for all URLs
335
- </p>`,
336
- })
337
-
338
389
  if (settingsOptions.footer) {
339
390
  const footer = addElement(settingsMain, "footer")
340
391
  footer.innerHTML =
@@ -351,6 +402,10 @@ function createSettingsElement() {
351
402
  }
352
403
 
353
404
  function addSideMenu() {
405
+ if (!getSettingsValue("displaySettingsButtonInSideMenu")) {
406
+ return
407
+ }
408
+
354
409
  const menu =
355
410
  $("#browser_extension_side_menu") ||
356
411
  addElement(doc.body, "div", {
@@ -379,6 +434,27 @@ function addSideMenu() {
379
434
  })
380
435
  }
381
436
 
437
+ function addCommonSettings(settingsTable: SettingsTable) {
438
+ let maxGroup = 0
439
+ for (const key in settingsTable) {
440
+ if (Object.hasOwn(settingsTable, key)) {
441
+ const item = settingsTable[key]
442
+ const group = item.group || 1
443
+ if (group > maxGroup) {
444
+ maxGroup = group
445
+ }
446
+ }
447
+ }
448
+
449
+ settingsTable.displaySettingsButtonInSideMenu = {
450
+ title: "Display Settings Button in Side Menu",
451
+ defaultValue: !(
452
+ typeof GM === "object" && typeof GM.registerMenuCommand === "function"
453
+ ),
454
+ group: maxGroup + 1,
455
+ }
456
+ }
457
+
382
458
  export async function showSettings() {
383
459
  const settingsContainer = getSettingsContainer()
384
460
 
@@ -395,10 +471,12 @@ export async function showSettings() {
395
471
  export const initSettings = async (options: SettingsOptions) => {
396
472
  settingsOptions = options
397
473
  settingsTable = options.settingsTable || {}
474
+ addCommonSettings(settingsTable)
398
475
  addValueChangeListener(storageKey, async () => {
399
476
  settings = await getSettings()
400
477
  // console.log(JSON.stringify(settings, null, 2))
401
478
  await updateOptions()
479
+ addSideMenu()
402
480
  if (typeof options.onValueChange === "function") {
403
481
  options.onValueChange()
404
482
  }
package/lib/style.scss CHANGED
@@ -213,7 +213,7 @@
213
213
  margin-right: 10px;
214
214
  }
215
215
 
216
- .option_groups .tip {
216
+ .option_groups .bes_tip {
217
217
  position: relative;
218
218
  margin: 0;
219
219
  padding: 0 15px 0 0;
@@ -221,11 +221,11 @@
221
221
  max-width: none;
222
222
  font-size: 14px;
223
223
 
224
- .tip_anchor {
224
+ .bes_tip_anchor {
225
225
  cursor: help;
226
226
  text-decoration: underline;
227
227
  }
228
- .tip_content {
228
+ .bes_tip_content {
229
229
  position: absolute;
230
230
  bottom: 15px;
231
231
  left: 0;
@@ -240,8 +240,8 @@
240
240
  box-shadow: 0px 10px 39px 10px rgba(62, 66, 66, 0.22) !important;
241
241
  }
242
242
 
243
- .tip_anchor:hover + .tip_content,
244
- .tip_content:hover {
243
+ .bes_tip_anchor:hover + .bes_tip_content,
244
+ .bes_tip_content:hover {
245
245
  display: block;
246
246
  }
247
247
 
package/lib/switch.ts CHANGED
@@ -4,7 +4,12 @@ import {
4
4
  createElement,
5
5
  } from "browser-extension-utils"
6
6
 
7
- export function createSwitch(options = {}): HTMLElement {
7
+ type SwichOptions = {
8
+ checked?: boolean
9
+ onchange?: (event: Event) => void | Promise<void>
10
+ }
11
+
12
+ export function createSwitch(options = {} as SwichOptions): HTMLElement {
8
13
  const container = createElement("label", { class: "container" })
9
14
  const checkbox = createElement(
10
15
  "input",
@@ -21,7 +26,10 @@ export function createSwitch(options = {}): HTMLElement {
21
26
  return container
22
27
  }
23
28
 
24
- export function createSwitchOption(text: string, options): HTMLElement {
29
+ export function createSwitchOption(
30
+ text: string,
31
+ options: SwichOptions
32
+ ): HTMLElement {
25
33
  const div = createElement("div", { class: "switch_option" })
26
34
  addElement(div, "span", { textContent: text })
27
35
  div.append(createSwitch(options))
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "browser-extension-settings",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Settings module for developing browser extensions and userscripts",
5
5
  "type": "module",
6
6
  "main": "./lib/index.ts",
7
7
  "scripts": {
8
8
  "p": "prettier --write .",
9
- "lint": "prettier --write . && xo --fix",
9
+ "lint": "run-s lint:*",
10
+ "lint:code": "prettier --write . && xo --fix",
11
+ "lint:type": "tsc --noemit",
10
12
  "test": "echo \"Error: no test specified\" && exit 0"
11
13
  },
12
14
  "repository": {
@@ -26,11 +28,13 @@
26
28
  "homepage": "https://github.com/utags/browser-extension-settings#readme",
27
29
  "dependencies": {
28
30
  "browser-extension-storage": "^0.1.2",
29
- "browser-extension-utils": "^0.1.7"
31
+ "browser-extension-utils": "^0.1.9"
30
32
  },
31
33
  "devDependencies": {
32
34
  "@types/chrome": "^0.0.237",
35
+ "npm-run-all": "^4.1.5",
33
36
  "prettier": "^2.8.8",
37
+ "typescript": "^5.1.6",
34
38
  "xo": "^0.54.2"
35
39
  },
36
40
  "files": [