@d-mok/quasar-app-extension-quasar-axe 1.0.3 → 1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d-mok/quasar-app-extension-quasar-axe",
3
- "version": "1.0.3",
3
+ "version": "1.0.7",
4
4
  "description": "A Quasar App Extension",
5
5
  "author": "d-mok <49301824+d-mok@users.noreply.github.com>",
6
6
  "license": "MIT",
@@ -20,3 +20,4 @@ export default boot(({ app }) => {
20
20
  app.component('QxSelectObject', QxSelectObject)
21
21
  app.component('QxSelectText', QxSelectText)
22
22
  })
23
+
@@ -12,29 +12,20 @@ export type Where<T> =
12
12
  | T[K][]
13
13
  | { like: string }
14
14
  }
15
- | ((_: PostgrestFilterBuilder) => any)
16
15
 
17
- // export type OrderBy<T> =
18
- // strKeyOf<T>
19
- // | { [K in keyof T]?: true | false }
20
16
 
21
17
 
22
-
23
- type Action = 'select' | 'insert' | 'update' | 'delete' | 'patch'
24
-
18
+ type Action = 'select' | 'insert' | 'update' | 'delete'
25
19
 
26
20
 
27
21
  function loadingBar(on: boolean): void {
28
- on ? setTimeout(() => LoadingBar.setDefaults({ size: "5px" }), 3000)
22
+ on
23
+ ? setTimeout(() => LoadingBar.setDefaults({ size: "5px" }), 3000)
29
24
  : LoadingBar.setDefaults({ size: "0px" })
30
25
  }
31
26
 
32
27
 
33
28
 
34
-
35
-
36
-
37
-
38
29
  /**
39
30
  * A subclass of array designed as an ORM.
40
31
  * @template T - type of element class
@@ -42,28 +33,38 @@ function loadingBar(on: boolean): void {
42
33
  */
43
34
  export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>{
44
35
 
36
+ constructor(...args: any) {
37
+ super(args)
38
+ if (this.caching) {
39
+ this.restoreCache()
40
+ setInterval(() => this.screenCache(), 6000 + Math.random() * 3000)
41
+ }
42
+ }
43
+
45
44
  /**
46
45
  * The name of the database table.
47
46
  */
48
47
  public abstract readonly tableName: string
48
+
49
49
  /**
50
50
  * The name of the ID field.
51
51
  */
52
52
  public abstract readonly idField: strKeyOf<R>
53
+
53
54
  /**
54
55
  * The class of the elements.
55
56
  */
56
57
  public abstract readonly entityClass: new (_: Partial<R>) => T
58
+
57
59
  /**
58
60
  * The default ordering instruction object.
59
61
  */
60
62
  public readonly ordering: Readonly<Ordering<T>> = []
61
- // /**
62
- // * The default fields to select
63
- // */
64
- // public readonly selectFields: strKeyOf<R>[] = []
65
-
66
63
 
64
+ /**
65
+ * Auto Cache
66
+ */
67
+ public readonly caching: boolean = false
67
68
 
68
69
  private convert(data: R[]): T[] {
69
70
  return data.map($ => new this.entityClass($))
@@ -75,41 +76,21 @@ export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>
75
76
  }
76
77
 
77
78
 
78
-
79
- // private defaultOrder(): void {
80
- // if (this.ordering !== undefined)
81
- // this.order(...this.ordering)
82
- // }
83
-
84
-
85
- // private selectedFields() {
86
- // return this.selectFields === undefined ? "*" : this.selectFields.join(",")
87
- // }
88
-
89
-
90
79
  protected idWhere(id: R[this['idField']]): any {
91
80
  return { [this.idField]: id }
92
81
  }
93
82
 
94
83
 
95
- protected idsWhere(ids: R[this['idField']][]): any {
96
- return { [this.idField]: ids }
97
- }
98
84
 
99
85
 
100
- protected orderById(ids: R[this['idField']][]): void {
101
- let ord = { [this.idField]: ids } as any
102
- this.order(ord)
103
- }
104
86
 
105
87
 
106
- private APIAction(type: Action, content: Partial<R>[] = []) {
88
+
89
+ private APIAction(type: Action, content: Partial<R>[]) {
107
90
  let q = supabase.from<R>(this.tableName)
108
91
  switch (type) {
109
92
  case 'select':
110
93
  return q.select('*')
111
- case 'patch':
112
- return q.select('*')
113
94
  case 'delete':
114
95
  return q.delete()
115
96
  case 'insert':
@@ -121,21 +102,19 @@ export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>
121
102
 
122
103
  protected async API({
123
104
  type,
124
- content,
125
- where,
126
- // orderBy,
127
- // limit,
128
- action = (_: any) => { },
129
- confirm,
130
- notify,
131
- slience
105
+ content = [],
106
+ where = {},
107
+ custom = $ => $,
108
+ action = ($: any) => { },
109
+ confirm = undefined,
110
+ notify = undefined,
111
+ slience = false
132
112
  }: {
133
113
  type: Action
134
114
  content?: Partial<R>[]
135
115
  where?: Where<R>
136
- // orderBy?: OrderBy<R>
137
- // limit?: number,
138
- action: (entities: T[]) => void
116
+ custom?: (_: PostgrestFilterBuilder) => PostgrestFilterBuilder
117
+ action?: (entities: T[]) => void
139
118
  confirm?: [string, string?]
140
119
  notify?: string
141
120
  slience?: boolean
@@ -145,12 +124,12 @@ export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>
145
124
 
146
125
  let q = this.APIAction(type, content)
147
126
  q = applyWhere(q, where)
148
- // q = applyOrderBy(q, orderBy)
149
- // q = applyLimit(q, limit)
127
+ q = custom(q)
150
128
 
151
129
  let { data, error } = await q
152
130
  handleError(data, error)
153
131
 
132
+ if (this.caching) this.pushCache(data)
154
133
  let entities = this.convert(data)
155
134
  action(entities)
156
135
  this.order(...this.ordering)
@@ -162,8 +141,6 @@ export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>
162
141
 
163
142
 
164
143
 
165
-
166
-
167
144
  /**
168
145
  * Get an item by `criteria`. If not found, return a new object.
169
146
  */
@@ -172,66 +149,75 @@ export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>
172
149
  }
173
150
 
174
151
 
175
- static new<Type>(): Type {
176
- return reactive(new (this.constructor as any)())
152
+ /**
153
+ * Get a reactive copy of this.
154
+ */
155
+ reactive(): this {
156
+ return reactive(this) as this
177
157
  }
178
158
 
179
159
 
180
160
 
181
- }
161
+ private get cache(): R[] {
162
+ return JSON.parse(localStorage.getItem(this.tableName) ?? '[]')
163
+ }
182
164
 
165
+ private set cache(data: R[]) {
166
+ let json = JSON.stringify(data)
167
+ localStorage.setItem(this.tableName, json)
168
+ }
183
169
 
170
+ private restoreCache() {
171
+ let entities = this.convert(this.cache)
172
+ this.set(entities)
173
+ if (process.env.DEBUGGING)
174
+ console.log(`[${this.tableName}] restore cache`)
175
+ }
184
176
 
177
+ private pushCache(data: R[]) {
178
+ this.cache = [...this.cache, ...data]
179
+ if (process.env.DEBUGGING)
180
+ console.log(`[${this.tableName}] push cache`)
181
+ }
182
+
183
+ private screenCache() {
184
+ let ids: any[] = this.scan(this.idField)
185
+ this.cache = this.cache.filter($ => ids.includes($[this.idField]))
186
+ if (process.env.DEBUGGING)
187
+ console.log(`[${this.tableName}] screen cache`)
188
+ }
185
189
 
186
190
 
187
191
 
188
- function applyWhere(q: PostgrestFilterBuilder, where?: Where<any>): PostgrestFilterBuilder {
189
- if (where === undefined)
190
- return q
191
192
 
192
- if (typeof where === 'function')
193
- return where(q)
194
193
 
195
- for (let key in where) {
196
- let val = where[key]
197
- if (Array.isArray(val)) {
198
- // treat as in
199
- q = q.in(key, val)
200
- } else if (typeof val === 'object') {
201
- if ('like' in val) {
202
- // treat as like
203
- let { like } = val as { like: string }
204
- q = q.like(key, like)
205
- }
206
- } else {
207
- // treat as eq
208
- q = q.eq(key, val)
209
- }
210
- }
211
- return q
212
194
  }
213
195
 
214
- // function applyOrderBy(q: PostgrestFilterBuilder, orderBy?: OrderBy<any>): PostgrestFilterBuilder {
215
- // if (orderBy === undefined)
216
- // return q
217
196
 
218
- // if (typeof orderBy === "string") {
219
- // q = q.order(orderBy)
220
- // } else {
221
- // let field = Object.keys(orderBy)[0]
222
- // let ascending = orderBy[field]
223
- // q = q.order(field, { ascending })
224
- // }
225
- // return q
226
- // }
197
+ function whereType(val: any): string {
198
+ if (Array.isArray(val))
199
+ return 'in'
227
200
 
201
+ if (typeof val === 'object' && 'like' in val)
202
+ return 'like'
228
203
 
204
+ return 'eq'
205
+ }
229
206
 
230
- // function applyLimit(q: PostgrestFilterBuilder, limit?: number): PostgrestFilterBuilder {
231
- // if (limit === undefined)
232
- // return q
233
207
 
234
- // return q.limit(limit)
235
- // }
236
208
 
209
+ function applyWhere(q: PostgrestFilterBuilder, where: Where<any>): PostgrestFilterBuilder {
210
+ for (let key in where) {
211
+ let val = where[key]
212
+ let type = whereType(val)
213
+
214
+ if (type === 'in')
215
+ q = q.in(key, val)
216
+ if (type === 'like')
217
+ q = q.like(key, val.like)
218
+ if (type === 'eq')
219
+ q = q.eq(key, val)
220
+ }
221
+ return q
222
+ }
237
223
 
@@ -28,7 +28,7 @@ export abstract class Table<T extends R, R extends object> extends TableView<T,
28
28
  await this.API({
29
29
  type: 'insert',
30
30
  content: Array.isArray(rows) ? rows : [rows],
31
- action: entities => this.push(...entities),
31
+ action: $ => this.push(...$),
32
32
  confirm,
33
33
  notify,
34
34
  slience
@@ -45,7 +45,7 @@ export abstract class Table<T extends R, R extends object> extends TableView<T,
45
45
  type: 'update',
46
46
  content: [row],
47
47
  where: this.idWhere(id),
48
- action: entities => this.merge(entities, this.idField),
48
+ action: $ => this.merge($, this.idField),
49
49
  confirm,
50
50
  notify,
51
51
  slience
@@ -88,7 +88,7 @@ export abstract class Table<T extends R, R extends object> extends TableView<T,
88
88
  await this.API({
89
89
  type: 'delete',
90
90
  where: this.idWhere(id),
91
- action: entities => this.discard(this.idWhere(id) as Criteria<T>),
91
+ action: $ => this.discard(this.idWhere(id) as Criteria<T>),
92
92
  confirm,
93
93
  notify,
94
94
  slience
@@ -151,3 +151,63 @@ export function Empower<RCls extends Constructor, R = InstanceType<RCls>>(BaseCl
151
151
  }
152
152
 
153
153
 
154
+
155
+
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+
164
+
165
+
166
+ // class FolderRow {
167
+ // id: number = 0
168
+ // subject: string = ""
169
+ // category: string = ""
170
+ // teacher: string = ""
171
+ // bank: string = ""
172
+ // name: string = ""
173
+ // pub_q: boolean = false
174
+ // pub_s: boolean = false
175
+ // codes: string[] = []
176
+ // modified: number = 0
177
+
178
+ // constructor(row = {}) {
179
+ // Object.assign(this, row)
180
+ // }
181
+ // }
182
+
183
+
184
+
185
+
186
+ // export class Folder extends FolderRow {
187
+
188
+
189
+
190
+
191
+ // }
192
+
193
+
194
+
195
+ // export class FolderTable extends Table<Folder, FolderRow>{
196
+ // readonly tableName = 'folder'
197
+ // readonly idField = 'id' as const
198
+ // readonly entityClass = Folder
199
+ // readonly ordering = [
200
+ // 'subject',
201
+ // { category: ['topic', 'paper', 'exercise'] },
202
+ // 'bank',
203
+ // 'name'
204
+ // ] as const
205
+
206
+
207
+
208
+ // }
209
+
210
+
211
+ // export const Folders = (new FolderTable).reactive()
212
+
213
+ // Folders.selectIn('teacher', [1,2,3])
@@ -1,13 +1,15 @@
1
1
  import { BasicTable, Where } from './basic'
2
+ import { PostgrestFilterBuilder } from '../supabase'
3
+ import { strKeyOf } from 'sapphire-js'
2
4
 
3
5
 
4
6
  type selectOptions<R> = {
5
7
  where?: Where<R>
6
- // orderBy?: OrderBy<R>
7
- // limit?: number
8
+ custom?: (_: PostgrestFilterBuilder) => PostgrestFilterBuilder
8
9
  confirm?: [string, string?]
9
10
  notify?: string
10
11
  slience?: boolean
12
+ patch?: boolean
11
13
  }
12
14
 
13
15
 
@@ -30,65 +32,42 @@ function chunk<T>(arr: T[], size: number): T[][] {
30
32
  */
31
33
  export abstract class TableView<T extends R, R extends object> extends BasicTable<T, R>{
32
34
 
33
- async select({ where, confirm, notify, slience }: selectOptions<R> = {}) {
35
+ async select({ where, custom, confirm, notify, slience, patch }: selectOptions<R> = {}) {
36
+ if (!patch) this.clear()
34
37
  await this.API({
35
38
  type: 'select',
36
39
  where,
37
- // orderBy,
38
- // limit,
39
- action: entities => this.set(entities),
40
- // order: true,
40
+ custom,
41
+ action: $ => patch ? this.absorb($, this.idField) : this.set($),
41
42
  confirm,
42
43
  notify,
43
44
  slience
44
45
  })
45
46
  }
46
47
 
47
- async patch({ where, confirm, notify, slience }: selectOptions<R> = {}) {
48
- await this.API({
49
- type: 'patch',
50
- where,
51
- // orderBy,
52
- // limit,
53
- action: entities => this.absorb(entities, this.idField),
54
- confirm,
55
- notify,
56
- slience
57
- })
58
- }
59
48
 
60
49
 
61
- async patchIDs(
62
- ids: R[this['idField']][],
63
- { where, confirm, notify, slience }: selectOptions<R> = {},
50
+ async selectIn<F extends strKeyOf<R>>(
51
+ field: F,
52
+ values: R[F][],
53
+ { where, custom, confirm, notify, slience, patch }: selectOptions<R> = {},
64
54
  chunkSize = 100,
65
55
  ) {
66
- for (let ck of chunk(ids, chunkSize)) {
67
- await this.patch({
68
- where: { ...this.idsWhere(ck), ...where },
69
- confirm,
70
- notify,
71
- slience
72
- })
73
- }
56
+ if (!patch) this.clear()
57
+ let getSelect = (ck: R[F][]): Promise<void> => this.select({
58
+ where: { [field]: ck, ...where },
59
+ custom,
60
+ confirm,
61
+ notify,
62
+ slience,
63
+ patch: true
64
+ })
65
+ await Promise.all(chunk(values, chunkSize).map(getSelect))
74
66
  if (this.ordering.length === 0)
75
- this.orderById(ids)
67
+ this.order({ [field]: values } as any)
76
68
  }
77
69
 
78
70
 
79
- async selectIDs(
80
- ids: R[this['idField']][],
81
- { where, confirm, notify, slience }: selectOptions<R> = {},
82
- chunkSize = 100,
83
- ) {
84
- this.clear()
85
- this.patchIDs(
86
- ids,
87
- { where, confirm, notify, slience },
88
- chunkSize
89
- )
90
- }
91
-
92
71
 
93
72
  }
94
73
 
@@ -137,7 +137,7 @@ export function handleError<T>(data: T[] | null, error: PostgrestError | null):
137
137
 
138
138
 
139
139
 
140
- const _filterBuilder = supabase.from('DEMO').select()
140
+ const _filterBuilder = supabase.from('DEMO').select().eq('','').order('f').limit(3)
141
141
  export type PostgrestFilterBuilder = typeof _filterBuilder
142
142
 
143
143