@humandialog/forms.svelte 0.4.27 → 0.4.28

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.
@@ -3,7 +3,8 @@ export declare enum rList_property_type {
3
3
  Hidden = 0,
4
4
  Text = 1,
5
5
  Date = 2,
6
- Combo = 3
6
+ Combo = 3,
7
+ Static = 4
7
8
  }
8
9
  export declare class rList_property {
9
10
  constructor(type: rList_property_type);
@@ -5,6 +5,7 @@ export var rList_property_type;
5
5
  rList_property_type[rList_property_type["Text"] = 1] = "Text";
6
6
  rList_property_type[rList_property_type["Date"] = 2] = "Date";
7
7
  rList_property_type[rList_property_type["Combo"] = 3] = "Combo";
8
+ rList_property_type[rList_property_type["Static"] = 4] = "Static";
8
9
  })(rList_property_type || (rList_property_type = {}));
9
10
  export class rList_property {
10
11
  constructor(type) {
@@ -212,6 +212,7 @@ async function edit_date(field, prop_idx) {
212
212
  </script>
213
213
 
214
214
  <!-- svelte-ignore a11y-click-events-have-key-events -->
215
+ {#if item}
215
216
  <section class="flex flex-row my-0 w-full text-sm text-slate-700 dark:text-slate-400 cursor-default rounded-md border-2 border-transparent {selected_class} {focused_class}"
216
217
  on:contextmenu={on_contextmenu}
217
218
  role="menu"
@@ -263,6 +264,10 @@ async function edit_date(field, prop_idx) {
263
264
  definition={prop.combo_definition}
264
265
  changed={(key,name)=>{on_combo_changed(key, name, prop)}}
265
266
  s='xs'/>
267
+ {:else if prop.type == rList_property_type.Static}
268
+ <span class="dark:text-white text-gray-400 truncate px-2.5 bg-slate-900/10 dark:bg-slate-100/10 rounded-lg">
269
+ {item[prop.a]}
270
+ </span>
266
271
  {/if}
267
272
  </span>
268
273
  {/if}
@@ -294,6 +299,7 @@ async function edit_date(field, prop_idx) {
294
299
  {/if}
295
300
  </div>
296
301
  </section>
302
+ {/if}
297
303
 
298
304
  <style>
299
305
  .grid-1
@@ -0,0 +1,12 @@
1
+ <script>import { getContext } from "svelte";
2
+ import { rList_property_type, rList_property } from "./List";
3
+ export let name;
4
+ export let a = "";
5
+ let definition = getContext("rList-definition");
6
+ let date_property = new rList_property(rList_property_type.Static);
7
+ date_property.name = name;
8
+ date_property.a = a;
9
+ if (!date_property.a)
10
+ date_property.a = date_property.name;
11
+ definition.properties.push(date_property);
12
+ </script>
@@ -0,0 +1,17 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ name: string;
5
+ a?: string | undefined;
6
+ };
7
+ events: {
8
+ [evt: string]: CustomEvent<any>;
9
+ };
10
+ slots: {};
11
+ };
12
+ export type ListProps = typeof __propDef.props;
13
+ export type ListEvents = typeof __propDef.events;
14
+ export type ListSlots = typeof __propDef.slots;
15
+ export default class List extends SvelteComponentTyped<ListProps, ListEvents, ListSlots> {
16
+ }
17
+ export {};
@@ -13,6 +13,7 @@ export let typename = "";
13
13
  export let c = "";
14
14
  export let toolbar_operations;
15
15
  export let context_menu;
16
+ export let key = "";
16
17
  let definition = new rList_definition();
17
18
  setContext("rList-definition", definition);
18
19
  setContext("rIs-table-component", true);
@@ -34,16 +35,18 @@ function setup(...args) {
34
35
  last_tick = $data_tick_store;
35
36
  item = self ?? $context_items_store[ctx];
36
37
  items = void 0;
37
- if (objects)
38
+ if (objects) {
38
39
  items = objects;
39
- else if (item && a)
40
+ } else if (item && a)
40
41
  items = item[a];
41
42
  if (items == void 0)
42
43
  items = [];
43
44
  if (items.length > 0) {
44
45
  let first_element = items[0];
45
46
  let keys = Object.keys(first_element);
46
- if (keys.includes("Id"))
47
+ if (key)
48
+ item_key = key;
49
+ else if (keys.includes("Id"))
47
50
  item_key = "Id";
48
51
  else if (keys.includes("$ref"))
49
52
  item_key = "$ref";
@@ -143,11 +146,12 @@ export function edit(element, property_name) {
143
146
  {context_menu}
144
147
  bind:this={rows[i]}
145
148
  >
149
+
146
150
  <span slot="left" let:element>
147
151
  <slot name="left" {element}/>
148
152
  </span>
149
153
  </List_element>
150
-
154
+
151
155
  {#if show_insertion_row_after_element == element}
152
156
  <Inserter oninsert={async (text) => {await insert(text, show_insertion_row_after_element)}}
153
157
  icon={definition.inserter_icon}
@@ -10,6 +10,7 @@ declare const __propDef: {
10
10
  c?: string | undefined;
11
11
  toolbar_operations: any;
12
12
  context_menu: any;
13
+ key?: string | undefined;
13
14
  refresh?: (() => void) | undefined;
14
15
  update_objects?: ((_objects: object[]) => void) | undefined;
15
16
  update_self?: ((_self: object) => void) | undefined;
@@ -94,6 +94,17 @@
94
94
  let rect = owner.getBoundingClientRect();
95
95
  let options = [];
96
96
 
97
+ if(config.customOperations && Array.isArray(config.customOperations) && config.customOperations.length > 0)
98
+ {
99
+ config.customOperations.forEach( o => {
100
+ options.push({
101
+ caption: o.caption,
102
+ icon: o.icon,
103
+ action: o.action
104
+ })
105
+ })
106
+ }
107
+
97
108
  if(show_sign_in_out_icons)
98
109
  {
99
110
  if(!is_logged_in)
package/index.d.ts CHANGED
@@ -34,7 +34,9 @@ export { default as ListSummary } from './components/list/list.summary.svelte';
34
34
  export { default as ListInserter } from './components/list/list.inserter.svelte';
35
35
  export { default as ListDateProperty } from './components/list/list.date.svelte';
36
36
  export { default as ListComboProperty } from './components/list/list.combo.svelte';
37
+ export { default as ListStaticProperty } from './components/list/list.static.svelte';
37
38
  export { default as Modal } from './modal.svelte';
39
+ export { default as MembersPage } from './tenant.members.svelte';
38
40
  export { select_item, activate_item, clear_active_item, is_active, is_selected, get_active, editable, start_editing, selectable, handle_select } from './utils';
39
41
  export { data_tick_store, has_selected_item, has_data_item } from "./stores";
40
42
  export { context_toolbar_operations, page_toolbar_operations, context_items_store, context_types_store } from './stores';
package/index.js CHANGED
@@ -40,7 +40,9 @@ export { default as ListSummary } from './components/list/list.summary.svelte';
40
40
  export { default as ListInserter } from './components/list/list.inserter.svelte';
41
41
  export { default as ListDateProperty } from './components/list/list.date.svelte';
42
42
  export { default as ListComboProperty } from './components/list/list.combo.svelte';
43
+ export { default as ListStaticProperty } from './components/list/list.static.svelte';
43
44
  export { default as Modal } from './modal.svelte';
45
+ export { default as MembersPage } from './tenant.members.svelte';
44
46
  export { select_item, activate_item, clear_active_item, is_active, is_selected, get_active, editable, start_editing, selectable, handle_select } from './utils';
45
47
  export { data_tick_store, has_selected_item, has_data_item } from "./stores";
46
48
  export { context_toolbar_operations, page_toolbar_operations, context_items_store, context_types_store } from './stores'; // tmp
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humandialog/forms.svelte",
3
- "version": "0.4.27",
3
+ "version": "0.4.28",
4
4
  "description": "Basic Svelte UI components for Object Reef applications",
5
5
  "devDependencies": {
6
6
  "@playwright/test": "^1.28.1",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "type": "module",
28
28
  "dependencies": {
29
- "@humandialog/auth.svelte": "^1.2.4",
29
+ "@humandialog/auth.svelte": "^1.2.7",
30
30
  "flowbite-svelte": "^0.29.13",
31
31
  "svelte-icons": "^2.1.0",
32
32
  "svelte-spa-router": "^3.3.0"
@@ -79,6 +79,7 @@
79
79
  "./components/list/list.combo.svelte": "./components/list/list.combo.svelte",
80
80
  "./components/list/list.date.svelte": "./components/list/list.date.svelte",
81
81
  "./components/list/list.inserter.svelte": "./components/list/list.inserter.svelte",
82
+ "./components/list/list.static.svelte": "./components/list/list.static.svelte",
82
83
  "./components/list/list.summary.svelte": "./components/list/list.summary.svelte",
83
84
  "./components/list/list.svelte": "./components/list/list.svelte",
84
85
  "./components/list/list.title.svelte": "./components/list/list.title.svelte",
@@ -107,6 +108,7 @@
107
108
  "./page.row.svelte": "./page.row.svelte",
108
109
  "./page.svelte": "./page.svelte",
109
110
  "./stores": "./stores.js",
111
+ "./tenant.members.svelte": "./tenant.members.svelte",
110
112
  "./tile.svelte": "./tile.svelte",
111
113
  "./tiles.row.svelte": "./tiles.row.svelte",
112
114
  "./tiles.vertical.row.svelte": "./tiles.vertical.row.svelte",
@@ -0,0 +1,397 @@
1
+ <script>
2
+ import { FaUserPlus,
3
+ FaUserMinus,
4
+ FaPen,
5
+ FaInfoCircle} from 'svelte-icons/fa'
6
+
7
+ import Page from './page.svelte'
8
+ import List from './components/list/list.svelte'
9
+ import ListTitle from './components/list/list.title.svelte'
10
+ import ListSummary from './components/list/list.summary.svelte'
11
+ import ListComboProperty from './components/list/list.combo.svelte'
12
+ import ComboItem from './components/combo/combo.item.svelte'
13
+ import ListStaticProperty from './components/list/list.static.svelte'
14
+ import Input from './components/inputbox.ltop.svelte';
15
+ import Icon from "./components/icon.svelte";
16
+ import Modal from './modal.svelte'
17
+ import Checkbox from './components/checkbox.svelte';
18
+ import {Popover} from 'flowbite-svelte'
19
+ import { reef } from '@humandialog/auth.svelte';
20
+
21
+
22
+ // ==============================================================================
23
+
24
+ export let users = undefined;
25
+ export let name_attrib = "Name";
26
+ export let email_attrib = "login";
27
+ export let ref_attrib = "$ref";
28
+ export let show_files = false;
29
+ //export let show_admin = true;
30
+ export let app_groups = undefined;
31
+
32
+ // ===============================================================================
33
+
34
+ $: init();
35
+
36
+ // ===============================================================================
37
+
38
+ let list;
39
+
40
+ let create_new_user_enabled = false;
41
+
42
+ let reef_users = [];
43
+ let new_reef_user_id = 1;
44
+
45
+ async function init()
46
+ {
47
+ reef_users = [];
48
+ if(users && Array.isArray(users) && users.length > 0)
49
+ {
50
+ users.forEach( u =>
51
+ {
52
+ reef_users.push(
53
+ {
54
+ [name_attrib]: u[name_attrib],
55
+ [email_attrib]: u[email_attrib],
56
+ [ref_attrib]: u[ref_attrib],
57
+ auth_group: 0,
58
+ files_group :0,
59
+ app_group: 0,
60
+ other: "",
61
+ avatar_url : "",
62
+ invitation_not_accepted: false,
63
+ removed: false,
64
+ membership_tag: "",
65
+ __hd_internal_item_id: new_reef_user_id++
66
+ }
67
+ )
68
+ })
69
+ }
70
+
71
+ await fetch_details();
72
+
73
+ }
74
+
75
+
76
+ async function fetch_details(...args)
77
+ {
78
+ let users_no = reef_users.length;
79
+ let handled_no = 0;
80
+ reef_users.forEach(async ru =>
81
+ {
82
+ let details = await reef.get(`/sys/user_details?email=${ru[email_attrib]}`)
83
+ set_user_info(ru, details);
84
+
85
+ handled_no++;
86
+ if(handled_no == reef_users.length)
87
+ list?.update_objects(reef_users);
88
+ } )
89
+ }
90
+
91
+ function set_user_info(user, info)
92
+ {
93
+ user.auth_group = info.auth_group ?? 0;
94
+ user.files_group = info.files_group ?? 0;
95
+ user.avatar_url = info.avatar_url ?? "";
96
+ user.other = info.access_details ?? "";
97
+ user.app_group = info.reef_user_group_id ?? 0;
98
+ user.removed = info.removed ?? false;
99
+ user.invitation_not_accepted = info.invitation_not_accepted ?? false;
100
+
101
+ if(user.removed)
102
+ user.membership_tag = "Removed";
103
+ else if(user.invitation_not_accepted)
104
+ user.membership_tag = "Invited";
105
+ else
106
+ user.membership_tag = "";
107
+
108
+ user.__hd_internal_item_id = new_reef_user_id++;
109
+ }
110
+
111
+ /*onMount(
112
+ async () => {
113
+ await fetch_details();
114
+ return () => {}
115
+ }
116
+ )
117
+ */
118
+
119
+ let new_user = {
120
+ name: '',
121
+ email: '',
122
+ maintainer: false
123
+ }
124
+
125
+ let name_input;
126
+ let email_input;
127
+
128
+ async function delete_user(user)
129
+ {
130
+ let email = user[email_attrib];
131
+
132
+ let removed_user_details = await reef.get('/sys/kick_out_user?email=' + email);
133
+ if(removed_user_details)
134
+ {
135
+ //let removed_user = reef_users.find( u => u[email_attrib] == user[email_attrib])
136
+ set_user_info(user, removed_user_details);
137
+ list?.refresh();
138
+ }
139
+ }
140
+
141
+ async function on_name_changed(user, name, property)
142
+ {
143
+ user[property] = name;
144
+
145
+ let user_path = user[ref_attrib];
146
+ if(!user_path)
147
+ return;
148
+
149
+ await reef.post(`${user_path}/set`,
150
+ {
151
+ [name_attrib]: name
152
+ });
153
+ }
154
+
155
+ async function on_change_privileges(user, flags, name)
156
+ {
157
+ if(user.auth_group != flags)
158
+ {
159
+ let email = user[email_attrib];
160
+ let info = await reef.get(`sys/set_user_details?email=${email}&auth_group=${flags}`)
161
+ if(info)
162
+ {
163
+ user.auth_group = flags;
164
+
165
+ set_user_info(user, info)
166
+ list?.refresh();
167
+ }
168
+ }
169
+ }
170
+
171
+ async function on_change_files_access(user, flags, name)
172
+ {
173
+ if(user.files_group != flags)
174
+ {
175
+ user.files_group = flags;
176
+
177
+ let email = user[email_attrib];
178
+ let info = await reef.get(`sys/set_user_details?email=${email}&files_group=${flags}`)
179
+ if(info)
180
+ {
181
+ set_user_info(user, info)
182
+ list?.refresh();
183
+ }
184
+ }
185
+ }
186
+
187
+ function create_new_user()
188
+ {
189
+ create_new_user_enabled = true;
190
+ }
191
+
192
+ let page_operations=[
193
+ {
194
+ icon: FaUserPlus,
195
+ caption: '',
196
+ action: (focused) => { create_new_user(); }
197
+ }
198
+ ]
199
+
200
+ function get_edit_operations(user)
201
+ {
202
+ let operations = [
203
+ {
204
+ caption: name_attrib,
205
+ action: (focused) => { list.edit(user, name_attrib) }
206
+ },
207
+ {
208
+ caption: 'Privileges',
209
+ action: (focused) => { list.edit(user, 'Privileges') }
210
+ }];
211
+
212
+ if(show_files)
213
+ {
214
+ operations.push({
215
+ caption: 'Files',
216
+ action: (focused) => { list.edit(user, 'Files') }
217
+ });
218
+ }
219
+
220
+ return operations;
221
+ }
222
+
223
+ let user_operations = (user) => {
224
+
225
+ let operations = [];
226
+
227
+ let edit_operations = get_edit_operations(user)
228
+ if(edit_operations.length == 1)
229
+ {
230
+ operations.push({
231
+ icon: FaPen,
232
+ caption: '',
233
+ action: edit_operations[0].action
234
+ });
235
+ }
236
+ else
237
+ {
238
+ operations.push({
239
+ icon: FaPen,
240
+ caption: '',
241
+ grid: edit_operations
242
+ });
243
+ }
244
+
245
+ operations.push({
246
+ caption: '',
247
+ icon: FaUserMinus,
248
+ action: (focused) => delete_user(user)
249
+ });
250
+
251
+ return operations;
252
+ }
253
+
254
+ let user_context_menu = (user) => {
255
+ let edit_operations = get_edit_operations(user);
256
+ return {
257
+ grid: edit_operations
258
+ }
259
+ }
260
+
261
+ let data_item =
262
+ {
263
+
264
+ }
265
+
266
+ function is_valid_email_address(e)
267
+ {
268
+ let pattern = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
269
+ return (e.match(pattern) != null);
270
+ }
271
+
272
+ function is_valid_name(s)
273
+ {
274
+ return !!s;
275
+ }
276
+
277
+ async function on_new_user_requested()
278
+ {
279
+ if(!name_input?.validate())
280
+ return;
281
+
282
+ if(!email_input?.validate())
283
+ return;
284
+
285
+ let result = await reef.post('/sys/invite_user',
286
+ {
287
+ email: new_user.email,
288
+ admin: new_user.maintainer,
289
+ set:
290
+ {
291
+ [name_attrib]: new_user.name,
292
+ [email_attrib]: new_user.email
293
+ }
294
+ })
295
+ if(result)
296
+ {
297
+ let created_user = result.User;
298
+ let new_reef_user = {
299
+ [name_attrib]: created_user[name_attrib],
300
+ [email_attrib]: created_user[email_attrib],
301
+ [ref_attrib]: created_user[ref_attrib]
302
+ }
303
+
304
+ let details = await reef.get(`/sys/user_details?email=${new_reef_user[email_attrib]}`)
305
+ set_user_info(new_reef_user, details);
306
+
307
+ reef_users = [...reef_users, new_reef_user]
308
+ list?.update_objects(reef_users);
309
+ }
310
+
311
+ new_user.name = '';
312
+ new_user.email = '';
313
+ new_user.maintainer = false;
314
+ create_new_user_enabled = false;
315
+ }
316
+
317
+ function on_new_user_canceled()
318
+ {
319
+ new_user.name = '';
320
+ new_user.email = '';
321
+ new_user.maintainer = false;
322
+ create_new_user_enabled = false;
323
+ }
324
+
325
+ </script>
326
+
327
+ <Page self={data_item}
328
+ cl="!bg-white dark:!bg-slate-900 w-full h-full flex flex-col overflow-y-hidden overflow-x-hidden py-1 px-1 border-0"
329
+ toolbar_operations={page_operations}
330
+ clears_context='props sel'>
331
+ <a href="/" class="underline text-sm font-semibold ml-3"> &lt; Back to root</a>
332
+
333
+ {#if reef_users && reef_users.length > 0}
334
+ <List objects={reef_users}
335
+ title='Members'
336
+ toolbar_operations={user_operations}
337
+ context_menu={user_context_menu}
338
+ key='__hd_internal_item_id'
339
+ bind:this={list}>
340
+ <ListTitle a={name_attrib} on_change={on_name_changed}/>
341
+ <ListSummary a={email_attrib} readonly/>
342
+
343
+ <ListStaticProperty name="Membership" a="membership_tag"/>
344
+
345
+ <ListComboProperty name='Privileges' a='auth_group' on_select={on_change_privileges}>
346
+ <ComboItem name='None' key={0} />
347
+ <ComboItem name='Can See' key={1} />
348
+ <ComboItem name='Can Invite' key={3} />
349
+ <ComboItem name='Maintainer' key={7} />
350
+ </ListComboProperty>
351
+
352
+ {#if show_files}
353
+ <ListComboProperty name='Files' a='files_group' on_select={on_change_files_access}>
354
+ <ComboItem name='None' key={0} />
355
+ <ComboItem name='Read' key={1} />
356
+ <ComboItem name='Write' key={2} />
357
+ <ComboItem name='Read&Write' key={3} />
358
+ </ListComboProperty>
359
+ {/if}
360
+
361
+
362
+ </List>
363
+ {/if}
364
+
365
+ </Page>
366
+
367
+ <Modal bind:open={create_new_user_enabled}
368
+ title='Invite someone'
369
+ ok_caption='Invite'
370
+ on_ok_callback={on_new_user_requested}
371
+ on_cancel_callback={on_new_user_canceled}
372
+ icon={FaUserPlus}>
373
+ <Input label='Name'
374
+ placeholder=''
375
+ self={new_user}
376
+ a="name"
377
+ validate_cb={is_valid_name}
378
+ bind:this={name_input}/>
379
+
380
+ <Input label='E-mail'
381
+ placeholder=''
382
+ self={new_user}
383
+ a="email"
384
+ validate_cb={is_valid_email_address}
385
+ bind:this={email_input}/>
386
+
387
+ <Checkbox class="mt-2 text-xs font-normal" self={new_user} a="maintainer">
388
+ <div class="flex flex-row items-center">
389
+ <span class="">Maintainer</span>
390
+ <Icon id="b1" size={4} component={FaInfoCircle} class="text-slate-400 ml-5 pt-0 mt-1"/>
391
+ <Popover class="w-64 text-sm font-light " title="Maintainer" triggeredBy="#b1">
392
+ Means that the invited user will be able to add and remove others and manage permissions in this organization.
393
+ </Popover>
394
+ </div>
395
+ </Checkbox>
396
+ </Modal>
397
+
@@ -0,0 +1,33 @@
1
+ /** @typedef {typeof __propDef.props} TenantProps */
2
+ /** @typedef {typeof __propDef.events} TenantEvents */
3
+ /** @typedef {typeof __propDef.slots} TenantSlots */
4
+ export default class Tenant extends SvelteComponentTyped<{
5
+ users?: any;
6
+ name_attrib?: string | undefined;
7
+ email_attrib?: string | undefined;
8
+ ref_attrib?: string | undefined;
9
+ show_files?: boolean | undefined;
10
+ app_groups?: any;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}> {
14
+ }
15
+ export type TenantProps = typeof __propDef.props;
16
+ export type TenantEvents = typeof __propDef.events;
17
+ export type TenantSlots = typeof __propDef.slots;
18
+ import { SvelteComponentTyped } from "svelte";
19
+ declare const __propDef: {
20
+ props: {
21
+ users?: any;
22
+ name_attrib?: string | undefined;
23
+ email_attrib?: string | undefined;
24
+ ref_attrib?: string | undefined;
25
+ show_files?: boolean | undefined;
26
+ app_groups?: any;
27
+ };
28
+ events: {
29
+ [evt: string]: CustomEvent<any>;
30
+ };
31
+ slots: {};
32
+ };
33
+ export {};
@@ -87,6 +87,17 @@
87
87
  let rect = owner.getBoundingClientRect();
88
88
  let options = [];
89
89
 
90
+ if(config.customOperations && Array.isArray(config.customOperations) && config.customOperations.length > 0)
91
+ {
92
+ config.customOperations.forEach( o => {
93
+ options.push({
94
+ caption: o.caption,
95
+ icon: o.icon,
96
+ action: o.action
97
+ })
98
+ })
99
+ }
100
+
90
101
  if(show_sign_in_out_icons)
91
102
  {
92
103
  if(!is_logged_in)