jabroni-outfit 1.6.4 → 2.0.7
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.
Potentially problematic release.
This version of jabroni-outfit might be problematic. Click here for more details.
- package/README.md +68 -108
- package/dist/index.d.ts +148 -62
- package/dist/jabroni-outfit.es.js +3804 -2969
- package/dist/jabroni-outfit.es.js.map +1 -1
- package/dist/jabroni-outfit.umd.js +13 -16
- package/dist/jabroni-outfit.umd.js.map +1 -1
- package/package.json +18 -18
- package/src/app.ts +39 -0
- package/src/components/App.vue +40 -0
- package/src/components/Badge.vue +30 -0
- package/src/components/Chevron.vue +10 -0
- package/src/components/Header.vue +38 -0
- package/src/components/RowButton.vue +21 -0
- package/src/components/Section.vue +66 -0
- package/src/components/icons/Check.vue +17 -0
- package/src/components/icons/ChevronDown.vue +17 -0
- package/src/components/icons/ChevronUp.vue +17 -0
- package/src/components/icons/Minus.vue +17 -0
- package/src/components/icons/Sun.vue +21 -0
- package/src/components/inputs/InputCheckbox.vue +29 -0
- package/src/components/inputs/InputNumber.vue +24 -0
- package/src/components/inputs/InputRange.vue +66 -0
- package/src/components/inputs/InputText.vue +21 -0
- package/src/index.ts +4 -17
- package/src/scheme/default-scheme.ts +129 -0
- package/src/scheme/parser.ts +103 -0
- package/src/scheme/scheme-element.ts +93 -0
- package/src/store/default-state.ts +8 -37
- package/src/store/index.ts +63 -0
- package/src/store/persistent-state.ts +45 -17
- package/src/style/index.css +30 -0
- package/src/test/example.ts +56 -44
- package/src/test/umd-example.js +58 -27
- package/src/types/index.ts +48 -0
- package/src/utils/index.ts +33 -0
- package/src/store/store.ts +0 -52
- package/src/store/types.ts +0 -16
- package/src/style.css +0 -3
- package/src/typings/vue.d.ts +0 -6
- package/src/ui/default-scheme.ts +0 -76
- package/src/ui/index.ts +0 -31
- package/src/ui/types.ts +0 -27
- package/src/ui/vue-templates/App.vue +0 -48
- package/src/ui/vue-templates/Gen.vue +0 -21
- package/src/ui/vue-templates/RowElement.vue +0 -66
- package/src/ui/vue-templates/icons/Close.vue +0 -7
- package/src/ui/vue-templates/icons/Config.vue +0 -7
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { ExtractValuesByKey, SchemeInput } from '../types';
|
|
2
|
+
|
|
3
|
+
export const DefaultScheme = [
|
|
4
|
+
{
|
|
5
|
+
title: 'Text Filter',
|
|
6
|
+
collapsed: true,
|
|
7
|
+
content: [
|
|
8
|
+
{ filterExclude: false, label: 'exclude' },
|
|
9
|
+
{
|
|
10
|
+
filterExcludeWords: '',
|
|
11
|
+
label: 'keywords',
|
|
12
|
+
watch: 'filterExclude',
|
|
13
|
+
placeholder: 'word, word1|word2, f:full_word...',
|
|
14
|
+
},
|
|
15
|
+
{ filterInclude: false, label: 'include' },
|
|
16
|
+
{
|
|
17
|
+
filterIncludeWords: '',
|
|
18
|
+
label: 'keywords',
|
|
19
|
+
watch: 'filterInclude',
|
|
20
|
+
placeholder: 'word, word1|word2, f:full_word...',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
title: 'Duration Filter',
|
|
26
|
+
collapsed: true,
|
|
27
|
+
content: [
|
|
28
|
+
{ filterDuration: false, label: 'enable' },
|
|
29
|
+
{ filterDurationMinutes: true, label: 'minutes (seconds by default)' },
|
|
30
|
+
{
|
|
31
|
+
filterDurationFrom: 0,
|
|
32
|
+
watch: 'filterDuration',
|
|
33
|
+
label: 'from',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
filterDurationTo: 600,
|
|
37
|
+
watch: 'filterDuration',
|
|
38
|
+
label: 'to',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
title: 'Sort By',
|
|
44
|
+
content: [
|
|
45
|
+
{
|
|
46
|
+
views: () => {
|
|
47
|
+
//@ts-expect-error
|
|
48
|
+
window.sortByViews?.();
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
duration: () => {
|
|
53
|
+
//@ts-expect-error
|
|
54
|
+
window.sortByDuration?.();
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
title: 'Privacy Filter',
|
|
61
|
+
content: [
|
|
62
|
+
{ filterPrivate: false, label: 'private' },
|
|
63
|
+
{ filterPublic: false, label: 'public' },
|
|
64
|
+
{
|
|
65
|
+
'check access 🔓': () => {
|
|
66
|
+
//@ts-expect-error
|
|
67
|
+
window.requestAccess?.();
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
title: 'Quality Filter',
|
|
74
|
+
content: [{ filterHD: false, label: 'HD' }],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
title: 'Advanced',
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
infiniteScrollEnabled: true,
|
|
81
|
+
label: 'infinite scroll',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
autoRequestAccess: false,
|
|
85
|
+
label: 'auto friend request',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
writeHistory: false,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
// Pagination
|
|
94
|
+
title: 'Badge',
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
text: 'return `${state.$paginationOffset}/${state.$paginationLast}`',
|
|
98
|
+
vif: 'return state.$paginationLast > 1',
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
] as const satisfies SchemeInput;
|
|
103
|
+
|
|
104
|
+
function selectFromScheme(keys: string[], scheme: SchemeInput): SchemeInput {
|
|
105
|
+
return scheme.filter((g) => g.title && keys.some((k) => k === g.title));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function setupScheme<
|
|
109
|
+
K extends ExtractValuesByKey<typeof DefaultScheme, 'title'>,
|
|
110
|
+
T extends SchemeInput,
|
|
111
|
+
TK extends ExtractValuesByKey<T, 'title'>,
|
|
112
|
+
>(
|
|
113
|
+
selectFromDefaults: K[],
|
|
114
|
+
customScheme: T = [] as unknown as T,
|
|
115
|
+
order?: (K | TK)[],
|
|
116
|
+
) {
|
|
117
|
+
const selectedScheme: SchemeInput = selectFromScheme(
|
|
118
|
+
selectFromDefaults,
|
|
119
|
+
DefaultScheme,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
selectedScheme.push(...customScheme);
|
|
123
|
+
|
|
124
|
+
if (order) {
|
|
125
|
+
return selectFromScheme(order, selectedScheme);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return selectedScheme;
|
|
129
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { JabronioStore } from '../store';
|
|
2
|
+
import type {
|
|
3
|
+
RawSchemeElement,
|
|
4
|
+
SchemeGroup,
|
|
5
|
+
SchemeInput,
|
|
6
|
+
SchemeParsed,
|
|
7
|
+
SchemeSection,
|
|
8
|
+
} from '../types';
|
|
9
|
+
import { SchemeElement } from './scheme-element';
|
|
10
|
+
|
|
11
|
+
export class SchemeParser {
|
|
12
|
+
public parsedScheme: SchemeParsed;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
public scheme: SchemeInput,
|
|
16
|
+
public store: JabronioStore = new JabronioStore({}),
|
|
17
|
+
) {
|
|
18
|
+
this.parsedScheme = this.parseScheme();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static parse(...args: ConstructorParameters<typeof SchemeParser>): {
|
|
22
|
+
scheme: SchemeParsed;
|
|
23
|
+
store: JabronioStore;
|
|
24
|
+
} {
|
|
25
|
+
const { parsedScheme, store } = new SchemeParser(...args);
|
|
26
|
+
// console.log('parsed scheme', parsedScheme);
|
|
27
|
+
// console.log('parsed store', JSON.parse(JSON.stringify(store)));
|
|
28
|
+
return { scheme: parsedScheme, store };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
parseSchemeElement(schemeElement: SchemeElement) {
|
|
32
|
+
this.parseStatePropsFromModel(schemeElement);
|
|
33
|
+
this.parseStatePropsFromExpressions(schemeElement.text);
|
|
34
|
+
this.parseStatePropsFromExpressions(schemeElement.vif);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
parseStatePropsFromModel(schemeElement: SchemeElement) {
|
|
38
|
+
const { name, value } = schemeElement;
|
|
39
|
+
if (
|
|
40
|
+
name === undefined ||
|
|
41
|
+
value === undefined ||
|
|
42
|
+
typeof value === 'function'
|
|
43
|
+
)
|
|
44
|
+
return;
|
|
45
|
+
this.store.add(name, value);
|
|
46
|
+
schemeElement.value = this.store.state[name];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
parseStatePropsFromExpressions(expr?: string) {
|
|
50
|
+
if (!expr) return;
|
|
51
|
+
expr.match(/state\.\$?\w+/g)?.forEach((name) => {
|
|
52
|
+
const stateProp = name.replace('state.', '');
|
|
53
|
+
this.store.add(stateProp, '', undefined, false);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
parseScheme() {
|
|
58
|
+
const parsedSchemeGroups: SchemeGroup<RawSchemeElement>[] = this.scheme.map(
|
|
59
|
+
(schemeSection: Partial<SchemeSection<RawSchemeElement>>, i) => {
|
|
60
|
+
const emptySchemeSection: SchemeGroup<RawSchemeElement> = {
|
|
61
|
+
content: [],
|
|
62
|
+
collapsed: false,
|
|
63
|
+
title: '',
|
|
64
|
+
id: `section ${i}`,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if (!schemeSection.content) {
|
|
68
|
+
this.store.add(emptySchemeSection.id, emptySchemeSection.collapsed);
|
|
69
|
+
return emptySchemeSection;
|
|
70
|
+
} else {
|
|
71
|
+
const mergedSchemeSection = {
|
|
72
|
+
...emptySchemeSection,
|
|
73
|
+
...schemeSection,
|
|
74
|
+
};
|
|
75
|
+
if (mergedSchemeSection.title.length > 0) {
|
|
76
|
+
mergedSchemeSection.id = mergedSchemeSection.title;
|
|
77
|
+
}
|
|
78
|
+
this.store.add(emptySchemeSection.id, emptySchemeSection.collapsed);
|
|
79
|
+
return mergedSchemeSection;
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const parsedScheme: SchemeParsed = parsedSchemeGroups.map(
|
|
85
|
+
(schemeGroup: SchemeGroup<RawSchemeElement>) => {
|
|
86
|
+
const { content, ...rest } = schemeGroup;
|
|
87
|
+
const newContent = content.map(
|
|
88
|
+
(rawSchemeElement: RawSchemeElement) =>
|
|
89
|
+
new SchemeElement(rawSchemeElement),
|
|
90
|
+
);
|
|
91
|
+
return { content: newContent, ...rest } as SchemeGroup<SchemeElement>;
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
parsedScheme.forEach((schemeGroup) => {
|
|
96
|
+
schemeGroup.content.forEach((c) => {
|
|
97
|
+
this.parseSchemeElement(c);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return parsedScheme;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HTMLInputType,
|
|
3
|
+
HTMLTag,
|
|
4
|
+
HTMLTagOrInputType,
|
|
5
|
+
Primitive,
|
|
6
|
+
RawSchemeElement,
|
|
7
|
+
} from '../types';
|
|
8
|
+
import { propsDifference, uuidv4 } from '../utils';
|
|
9
|
+
|
|
10
|
+
export class SchemeElement {
|
|
11
|
+
public name?: string;
|
|
12
|
+
public value?: Primitive | (() => void);
|
|
13
|
+
public watch?: string;
|
|
14
|
+
public step?: string;
|
|
15
|
+
public min?: string;
|
|
16
|
+
public max?: string;
|
|
17
|
+
public vif?: string;
|
|
18
|
+
public text?: string;
|
|
19
|
+
public type: HTMLTagOrInputType = 'div';
|
|
20
|
+
public label?: string;
|
|
21
|
+
public placeholder?: string;
|
|
22
|
+
public id: string;
|
|
23
|
+
|
|
24
|
+
constructor(schemeElement: RawSchemeElement) {
|
|
25
|
+
const { d2 } = propsDifference(this, schemeElement);
|
|
26
|
+
Object.assign(this, schemeElement);
|
|
27
|
+
this.parseModel(d2);
|
|
28
|
+
this.parseType();
|
|
29
|
+
this.parseLabel();
|
|
30
|
+
this.id = this.name || uuidv4();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private parseType() {
|
|
34
|
+
if (this.type !== 'div') return;
|
|
35
|
+
if (this.value !== undefined) {
|
|
36
|
+
let parsedType: string = typeof this.value;
|
|
37
|
+
if (parsedType === 'function') {
|
|
38
|
+
parsedType = 'button';
|
|
39
|
+
} else if (parsedType === 'string') {
|
|
40
|
+
parsedType = 'text';
|
|
41
|
+
} else if (parsedType === 'number') {
|
|
42
|
+
parsedType = 'number';
|
|
43
|
+
this.parseNumber();
|
|
44
|
+
} else if (parsedType === 'boolean') {
|
|
45
|
+
parsedType = 'checkbox';
|
|
46
|
+
}
|
|
47
|
+
this.type = parsedType as HTMLInputType;
|
|
48
|
+
} else if (this.text) {
|
|
49
|
+
this.type = 'span';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private parseNumber() {
|
|
54
|
+
if (this.min || this.max || this.step) return;
|
|
55
|
+
this.min = '0';
|
|
56
|
+
this.step = '10';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private parseLabel() {
|
|
60
|
+
if (
|
|
61
|
+
this.label !== undefined ||
|
|
62
|
+
this.type === 'button'
|
|
63
|
+
)
|
|
64
|
+
return;
|
|
65
|
+
this.label = this.name;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private parseModel(differenceKeys: string[]) {
|
|
69
|
+
if (this.name && this.value) return;
|
|
70
|
+
const name = differenceKeys[0];
|
|
71
|
+
if (name) {
|
|
72
|
+
this.name = name;
|
|
73
|
+
this.value = this[name] as Primitive | (() => void);
|
|
74
|
+
delete this[name];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get isInput() {
|
|
79
|
+
return /checkbox|text|number/.test(this.type);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get htmlTag(): HTMLTag {
|
|
83
|
+
return this.isInput ? 'input' : (this.type as HTMLTag);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get inputType(): HTMLInputType {
|
|
87
|
+
return this.isInput ? (this.type as HTMLInputType) : '';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get callback(): (() => void) | undefined {
|
|
91
|
+
return this.htmlTag === 'button' ? (this.value as () => void) : undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -1,38 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { StoreStateOptions } from '../types';
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
filterIncludeWords: { value: "", persistent: true, watch: "filterInclude" },
|
|
11
|
-
},
|
|
12
|
-
MISC: {
|
|
13
|
-
infiniteScrollEnabled: { value: true, persistent: true, watch: true },
|
|
14
|
-
uiEnabled: { value: true, persistent: true, watch: true }
|
|
15
|
-
},
|
|
16
|
-
DURATION_FILTER: {
|
|
17
|
-
filterDuration: { value: false, persistent: true, watch: true },
|
|
18
|
-
filterDurationFrom: { value: 0, type: 'number', persistent: true, watch: 'filterDuration' },
|
|
19
|
-
filterDurationTo: { value: 600, type: 'number', persistent: true, watch: 'filterDuration' }
|
|
20
|
-
},
|
|
21
|
-
PRIVACY_FILTER: {
|
|
22
|
-
filterPrivate: { value: false, persistent: true, watch: true },
|
|
23
|
-
filterPublic: { value: false, persistent: true, watch: true }
|
|
24
|
-
},
|
|
25
|
-
HD_FILTER: {
|
|
26
|
-
filterHD: { value: false, persistent: true, watch: true }
|
|
27
|
-
},
|
|
28
|
-
PAGINATION: {
|
|
29
|
-
pagIndexLast: { value: 1, persistent: false, watch: true },
|
|
30
|
-
pagIndexCur: { value: 1, persistent: false, watch: true }
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const defaultStateInclExclMiscPagination: StateOptions = Object.assign({}, stateOptions.EXCLUDE, stateOptions.INCLUDE, stateOptions.MISC, stateOptions.PAGINATION);
|
|
35
|
-
export const defaultStateWithDuration: StateOptions = Object.assign({}, defaultStateInclExclMiscPagination, stateOptions.DURATION_FILTER);
|
|
36
|
-
export const defaultStateWithDurationAndHD: StateOptions = Object.assign({}, defaultStateWithDuration, stateOptions.HD_FILTER);
|
|
37
|
-
export const defaultStateWithDurationAndPrivacy: StateOptions = Object.assign({}, defaultStateWithDuration, stateOptions.PRIVACY_FILTER);
|
|
38
|
-
export const defaultStateWithDurationAndPrivacyAndHD: StateOptions = Object.assign({}, defaultStateWithDurationAndPrivacy, stateOptions.HD_FILTER);
|
|
3
|
+
export const StoreStateDefault: StoreStateOptions = {
|
|
4
|
+
uiEnabled: true,
|
|
5
|
+
hidden: false,
|
|
6
|
+
darkmode: true,
|
|
7
|
+
$paginationLast: 1,
|
|
8
|
+
$paginationOffset: 1,
|
|
9
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { parseIntegerOr } from 'billy-herrington-utils';
|
|
2
|
+
import { watch } from 'vue';
|
|
3
|
+
import type {
|
|
4
|
+
Callback,
|
|
5
|
+
Primitive,
|
|
6
|
+
StoreState,
|
|
7
|
+
StoreStateOptions,
|
|
8
|
+
StoreStateRaw,
|
|
9
|
+
} from '../types';
|
|
10
|
+
import { StoreStateDefault } from './default-state';
|
|
11
|
+
import { PersistentState } from './persistent-state';
|
|
12
|
+
|
|
13
|
+
export class JabronioStore {
|
|
14
|
+
private callbacks: Callback<StoreStateRaw>[] = [];
|
|
15
|
+
public state: StoreState;
|
|
16
|
+
|
|
17
|
+
constructor(options?: StoreStateOptions) {
|
|
18
|
+
const storeOptions = Object.assign({}, StoreStateDefault, options);
|
|
19
|
+
this.state = new PersistentState({}).state;
|
|
20
|
+
this.parseState(storeOptions);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
subscribe(callback: Callback<StoreStateRaw>) {
|
|
24
|
+
this.callbacks.push(callback);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
notify(subject: StoreStateRaw) {
|
|
28
|
+
this.callbacks.forEach((cb) => {
|
|
29
|
+
cb(subject);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
add(key: string, value: Primitive, watchKey?: string, safe?: boolean) {
|
|
34
|
+
this.state[key] =
|
|
35
|
+
key in this.state ? (this.state[key] as Primitive) : value;
|
|
36
|
+
|
|
37
|
+
watch(
|
|
38
|
+
() => this.state[key],
|
|
39
|
+
(a, b) => {
|
|
40
|
+
if (safe !== false) {
|
|
41
|
+
if (typeof value === 'number') {
|
|
42
|
+
this.state[key] = parseIntegerOr(a as string, b as number);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const subject = typeof watchKey === 'string' ? watchKey : key;
|
|
46
|
+
this.notify({ [subject]: this.state[subject] as Primitive });
|
|
47
|
+
},
|
|
48
|
+
{ deep: true },
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
parseState(options: StoreStateOptions) {
|
|
55
|
+
Object.entries(options).forEach(([k, v]) => {
|
|
56
|
+
if (typeof v === 'object') {
|
|
57
|
+
this.add(k, v.value, v.watch);
|
|
58
|
+
} else {
|
|
59
|
+
this.add(k, v);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,36 +1,64 @@
|
|
|
1
|
-
import { type Reactive, reactive, watch } from 'vue'
|
|
2
|
-
import type {
|
|
1
|
+
import { type Reactive, reactive, type WatchStopHandle, watch } from 'vue';
|
|
2
|
+
import type { StoreStateRaw } from '../types';
|
|
3
|
+
|
|
4
|
+
const LOCAL_STORAGE_KEY = 'state_acephale';
|
|
3
5
|
|
|
4
6
|
export class PersistentState {
|
|
5
|
-
public state: Reactive<
|
|
7
|
+
public state: Reactive<StoreStateRaw>;
|
|
8
|
+
private watchStopHandler: WatchStopHandle;
|
|
6
9
|
|
|
7
|
-
constructor(
|
|
10
|
+
constructor(
|
|
11
|
+
storeStateRaw: StoreStateRaw,
|
|
12
|
+
private key = LOCAL_STORAGE_KEY,
|
|
13
|
+
) {
|
|
8
14
|
this.key = key;
|
|
9
|
-
this.state = reactive(
|
|
15
|
+
this.state = reactive(storeStateRaw);
|
|
10
16
|
this.sync();
|
|
11
|
-
this.watchPersistence();
|
|
17
|
+
this.watchStopHandler = this.watchPersistence();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
dispose() {
|
|
21
|
+
this.watchStopHandler();
|
|
22
|
+
window.removeEventListener('focus', this.setFromLocalStorage);
|
|
23
|
+
document.removeEventListener('visibilitychange', this.setFromLocalStorage);
|
|
12
24
|
}
|
|
13
25
|
|
|
14
26
|
sync() {
|
|
15
|
-
this.
|
|
16
|
-
window.addEventListener('focus', this.
|
|
27
|
+
this.setFromLocalStorage();
|
|
28
|
+
window.addEventListener('focus', this.setFromLocalStorage);
|
|
29
|
+
document.addEventListener('visibilitychange', this.setFromLocalStorage);
|
|
17
30
|
}
|
|
18
31
|
|
|
19
32
|
watchPersistence() {
|
|
20
|
-
watch(
|
|
21
|
-
this.
|
|
22
|
-
|
|
33
|
+
return watch(
|
|
34
|
+
this.state,
|
|
35
|
+
() => {
|
|
36
|
+
this.saveToLocalStorage();
|
|
37
|
+
},
|
|
38
|
+
{ immediate: false, deep: true },
|
|
39
|
+
);
|
|
23
40
|
}
|
|
24
41
|
|
|
25
|
-
|
|
26
|
-
|
|
42
|
+
get persistentOnly() {
|
|
43
|
+
const persistentKeys = Object.keys(this.state).filter(
|
|
44
|
+
(k) => !k.startsWith('$'),
|
|
45
|
+
);
|
|
46
|
+
const persistentOnly = Object.assign(
|
|
47
|
+
{},
|
|
48
|
+
...persistentKeys.map((k) => ({ [k]: this.state[k] })),
|
|
49
|
+
);
|
|
50
|
+
return persistentOnly;
|
|
27
51
|
}
|
|
28
52
|
|
|
29
|
-
|
|
53
|
+
saveToLocalStorage() {
|
|
54
|
+
localStorage.setItem(this.key, JSON.stringify(this.persistentOnly));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setFromLocalStorage = () => {
|
|
30
58
|
const localStorageValue = localStorage.getItem(this.key);
|
|
31
59
|
if (localStorageValue !== null) {
|
|
32
|
-
const parsedState = JSON.parse(localStorageValue) as
|
|
60
|
+
const parsedState = JSON.parse(localStorageValue) as StoreStateRaw;
|
|
33
61
|
Object.assign(this.state, parsedState);
|
|
34
62
|
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
@import "tailwindcss/theme";
|
|
2
|
+
|
|
3
|
+
@layer base, components, utilities;
|
|
4
|
+
|
|
5
|
+
@layer base {
|
|
6
|
+
.taper-class {
|
|
7
|
+
@import "tailwindcss/preflight";
|
|
8
|
+
|
|
9
|
+
&, & *, & ::after, & ::before {
|
|
10
|
+
all: revert-layer;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@layer utilities {
|
|
16
|
+
.taper-class {
|
|
17
|
+
@import "tailwindcss/utilities";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
|
22
|
+
|
|
23
|
+
@layer components {
|
|
24
|
+
.tw-input-colors {
|
|
25
|
+
@apply border-black focus:outline-none focus:bg-yellow-50;
|
|
26
|
+
@apply dark:text-zinc-300 dark:bg-zinc-700 dark:focus:outline-gray-600
|
|
27
|
+
dark:hover:bg-zinc-600 dark:focus:bg-zinc-700;
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/test/example.ts
CHANGED
|
@@ -1,52 +1,64 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { defaultSchemeWithPrivacyFilterWithHDwithSort } from "../ui/default-scheme";
|
|
6
|
-
import type { Scheme } from "../ui/types";
|
|
7
|
-
|
|
8
|
-
const example1 = () => {
|
|
9
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
10
|
-
(defaultSchemeWithPrivacyFilterWithHDwithSort as any).privacyFilter.push(
|
|
11
|
-
{ type: "button", innerText: "check access 🔓", callback: () => { } });
|
|
12
|
-
|
|
13
|
-
const store = new JabroniOutfitStore(defaultStateWithDurationAndPrivacyAndHD);
|
|
14
|
-
|
|
15
|
-
new JabroniOutfitUI(store, defaultSchemeWithPrivacyFilterWithHDwithSort);
|
|
16
|
-
|
|
17
|
-
store.subscribe((subj) => {
|
|
18
|
-
const satisfy = /filter/gi.test(Object.keys(subj)[0]);
|
|
19
|
-
console.log({ ...subj, satisfy });
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
example1();
|
|
1
|
+
import { JabronioGUI } from '../app';
|
|
2
|
+
import { DefaultScheme, setupScheme } from '../scheme/default-scheme';
|
|
3
|
+
import { JabronioStore } from '../store';
|
|
4
|
+
import type { SchemeInput, StoreStateOptions } from '../types';
|
|
24
5
|
|
|
25
6
|
const example2 = () => {
|
|
26
|
-
const customState:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
gradientEnabled: { value: true, persistent: false, watch: true, type: "boolean" },
|
|
31
|
-
uiEnabled: { value: true, persistent: true, watch: true, type: "boolean" }
|
|
32
|
-
}
|
|
7
|
+
const customState: StoreStateOptions = {
|
|
8
|
+
uiEnabled: true,
|
|
9
|
+
hidden: false,
|
|
10
|
+
};
|
|
33
11
|
|
|
34
|
-
const store = new
|
|
12
|
+
const store = new JabronioStore(customState);
|
|
35
13
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
14
|
+
const scheme: SchemeInput = setupScheme(
|
|
15
|
+
DefaultScheme.map((t) => t.title).filter(Boolean),
|
|
16
|
+
// [],
|
|
17
|
+
[
|
|
18
|
+
{
|
|
19
|
+
title: 'Colors',
|
|
20
|
+
collapsed: true,
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
$color1: 'coral',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
color2: 'crimson',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
$color3: 'tomato',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
size: 100,
|
|
33
|
+
type: 'range',
|
|
34
|
+
max: '500',
|
|
35
|
+
min: '0',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
gradientEnabled: true,
|
|
39
|
+
label: 'gradient enabled',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
reset: () => {
|
|
43
|
+
store.state.$color1 = 'darkslateblue';
|
|
44
|
+
store.state.color2 = 'maroon';
|
|
45
|
+
store.state.$color3 = 'darksalmon';
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
);
|
|
42
52
|
|
|
43
|
-
new
|
|
53
|
+
new JabronioGUI(scheme, store);
|
|
44
54
|
|
|
45
55
|
function drawGradient() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
const { $color1, color2, $color3, gradientEnabled, size } = store.state;
|
|
57
|
+
if (!gradientEnabled) {
|
|
58
|
+
document.body.style.background = '#000';
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
document.body.style.background = `repeating-radial-gradient(${$color1}, ${color2}, ${$color3} ${size}%)`;
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
drawGradient();
|
|
@@ -54,6 +66,6 @@ const example2 = () => {
|
|
|
54
66
|
store.subscribe(() => {
|
|
55
67
|
drawGradient();
|
|
56
68
|
});
|
|
57
|
-
}
|
|
69
|
+
};
|
|
58
70
|
|
|
59
|
-
example2();
|
|
71
|
+
example2();
|