browser-extension-settings 0.0.8 → 0.2.0

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,20 +33,44 @@ 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
43
47
  defaultValue: boolean
48
+ type?: "switch"
49
+ group?: number
44
50
  }
45
51
 
46
52
  type SettingsInputItem = {
47
53
  title: string
48
54
  defaultValue: string
55
+ placeholder?: string
49
56
  type: string
57
+ group?: number
58
+ }
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
50
74
  }
51
75
 
52
76
  type RelatedExtension = {
@@ -91,10 +115,33 @@ async function saveSattingsValue(key: string, value: any) {
91
115
  await setValue(storageKey, settings)
92
116
  }
93
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
+
94
141
  export function getSettingsValue(key: string): boolean | string | undefined {
95
142
  return Object.hasOwn(settings, key)
96
- ? settings[key]
97
- : settingsTable[key]?.defaultValue
143
+ ? (settings[key] as boolean | string | undefined)
144
+ : (settingsTable[key]?.defaultValue as boolean | string | undefined)
98
145
  }
99
146
 
100
147
  const closeModal = () => {
@@ -136,28 +183,43 @@ async function updateOptions() {
136
183
 
137
184
  for (const key in settingsTable) {
138
185
  if (Object.hasOwn(settingsTable, key)) {
139
- const checkbox = $(
140
- `#${settingsElementId} .option_groups .switch_option[data-key="${key}"] input`
141
- )
142
- if (checkbox) {
143
- checkbox.checked = getSettingsValue(key)
186
+ const item = settingsTable[key]
187
+ const type = item.type || "switch"
188
+
189
+ // console.log(key, type)
190
+ switch (type) {
191
+ case "switch": {
192
+ const checkbox = $(
193
+ `#${settingsElementId} .option_groups .switch_option[data-key="${key}"] input`
194
+ ) as HTMLInputElement
195
+ if (checkbox) {
196
+ checkbox.checked = getSettingsValue(key) as boolean
197
+ }
198
+
199
+ break
200
+ }
201
+
202
+ case "textarea": {
203
+ const textArea = $(
204
+ `#${settingsElementId} .option_groups textarea[data-key="${key}"]`
205
+ ) as HTMLTextAreaElement
206
+ if (textArea) {
207
+ textArea.value = getSettingsValue(key) as string
208
+ }
209
+
210
+ break
211
+ }
212
+
213
+ default: {
214
+ break
215
+ }
144
216
  }
145
217
  }
146
218
  }
147
219
 
148
- const host = location.host
149
- const group2 = $(`#${settingsElementId} .option_groups:nth-of-type(2)`)
150
- if (group2) {
151
- group2.style.display = getSettingsValue(
152
- `enableCustomRulesForCurrentSite_${host}`
153
- )
154
- ? "block"
155
- : "none"
156
- }
157
-
158
- const customStyleValue = $(`#${settingsElementId} .option_groups textarea`)
159
- if (customStyleValue) {
160
- customStyleValue.value = settings[`customRulesForCurrentSite_${host}`] || ""
220
+ if (typeof settingsOptions.onViewUpdate === "function") {
221
+ const settingsMain = createSettingsElement()
222
+ settingsOptions.onViewUpdate(settingsMain)
161
223
  }
162
224
  }
163
225
 
@@ -231,74 +293,98 @@ function createSettingsElement() {
231
293
  addElement(settingsMain, "h2", { textContent: settingsOptions.title })
232
294
  }
233
295
 
234
- const options = addElement(settingsMain, "div", { class: "option_groups" })
235
- for (const key in settingsTable) {
236
- if (Object.hasOwn(settingsTable, key)) {
237
- const item = settingsTable[key]
238
- if (!item.type || item.type === "switch") {
239
- const switchOption = createSwitchOption(item.title, {
240
- async onchange(event: Event) {
241
- if (event.target) {
242
- await saveSattingsValue(key, event.target.checked)
243
- }
244
- },
245
- })
246
-
247
- switchOption.dataset.key = key
248
-
249
- addElement(options, switchOption)
296
+ const optionGroups: HTMLElement[] = []
297
+ const getOptionGroup = (index: number) => {
298
+ if (index > optionGroups.length) {
299
+ for (let i = optionGroups.length; i < index; i++) {
300
+ optionGroups.push(
301
+ addElement(settingsMain!, "div", {
302
+ class: "option_groups",
303
+ })
304
+ )
250
305
  }
251
306
  }
307
+
308
+ return optionGroups[index - 1]
252
309
  }
253
310
 
254
- const options2 = addElement(settingsMain, "div", {
255
- class: "option_groups",
256
- })
257
- let timeoutId: number | undefined
258
- addElement(options2, "textarea", {
259
- placeholder: `/* Custom rules for internal URLs, matching URLs will be opened in new tabs */`,
260
- onkeyup(event: Event) {
261
- const textArea = event.target as HTMLTextAreaElement
262
- if (timeoutId) {
263
- clearTimeout(timeoutId)
264
- timeoutId = undefined
265
- }
311
+ for (const key in settingsTable) {
312
+ if (Object.hasOwn(settingsTable, key)) {
313
+ const item = settingsTable[key]
314
+ const type = item.type || "switch"
315
+ const group = item.group || 1
316
+ const optionGroup = getOptionGroup(group)
317
+ // console.log(key, item, type, group)
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)
325
+ }
326
+ },
327
+ })
328
+
329
+ switchOption.dataset.key = key
330
+
331
+ addElement(optionGroup, switchOption)
332
+
333
+ break
334
+ }
266
335
 
267
- timeoutId = setTimeout(async () => {
268
- const host = location.host
269
- if (textArea) {
270
- await saveSattingsValue(
271
- `customRulesForCurrentSite_${host}`,
272
- textArea.value.trim()
273
- )
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
274
357
  }
275
- }, 100)
276
- },
277
- })
278
358
 
279
- const tip = addElement(options2, "div", {
280
- class: "tip",
281
- })
282
- addElement(tip, "a", {
283
- class: "tip_anchor",
284
- textContent: "Examples",
285
- })
286
- const tipContent = addElement(tip, "div", {
287
- class: "tip_content",
288
- // eslint-disable-next-line @typescript-eslint/naming-convention
289
- innerHTML: `<p>Custom rules for internal URLs, matching URLs will be opened in new tabs</p>
290
- <p>
291
- - One line per url pattern<br>
292
- - All URLs contains '/posts' or '/users/'<br>
293
- <pre>/posts/
294
- /users/</pre>
295
-
296
- - Regex is supported<br>
297
- <pre>^/(posts|members)/d+</pre>
298
-
299
- - '*' for all URLs
300
- </p>`,
301
- })
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
385
+ }
386
+ }
387
+ }
302
388
 
303
389
  if (settingsOptions.footer) {
304
390
  const footer = addElement(settingsMain, "footer")
@@ -362,6 +448,7 @@ export const initSettings = async (options: SettingsOptions) => {
362
448
  settingsTable = options.settingsTable || {}
363
449
  addValueChangeListener(storageKey, async () => {
364
450
  settings = await getSettings()
451
+ // console.log(JSON.stringify(settings, null, 2))
365
452
  await updateOptions()
366
453
  if (typeof options.onValueChange === "function") {
367
454
  options.onValueChange()
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.0.8",
3
+ "version": "0.2.0",
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": {
@@ -30,7 +32,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": [