@isoftdata/svelte-user-configuration 1.3.2 → 2.0.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/README.md CHANGED
@@ -8,6 +8,13 @@ This Svelte component provides a user configuration interface, including user ac
8
8
  npm i @isoftdata/svelte-user-configuration
9
9
  ```
10
10
 
11
+ ## Breaking changes
12
+
13
+ ### 2.0.0
14
+ - Require Svelte 5
15
+ - Slots -> Snippets
16
+ - Events -> Callbacks
17
+
11
18
  ## Props
12
19
 
13
20
  ### User Account Properties
@@ -42,7 +49,7 @@ npm i @isoftdata/svelte-user-configuration
42
49
  | Prop | Type | Required | Default | Description |
43
50
  |------|------|----------|---------|-------------|
44
51
  | `permissions` | `Permissions` | ✅ | - | The list of permissions for the user. |
45
- | `permissionValueMap` | `PermissionValueMap` | ✅ | `new Map()` | A mapping of permissions to their values. |
52
+ | `permissionValueMap` | `PermissionValueMap` | ✅ | `new SvelteMap()` | A mapping of permissions to their values. **Note this *must* be a `SvelteMap`** or reactivity will not work. |
46
53
  | `groupPermissionValueMap` | `GroupPermissionValueMap` | ❌ | `undefined` | A mapping of group-based permissions. |
47
54
 
48
55
  ### Other Properties
@@ -145,7 +152,7 @@ This component provides named slots for injecting additional content.
145
152
  for (const id of permissionIds) {
146
153
  permissionValueMap.set(id, value)
147
154
  }
148
- permissionValueMap = new Map(permissionValueMap)
155
+ permissionValueMap = new SvelteMap(permissionValueMap)
149
156
  }}
150
157
  />
151
158
  ```
@@ -1,21 +1,33 @@
1
- <script>import { getContext, tick } from "svelte";
2
- import { klona } from "klona";
3
- import { translate as defaultTranslate } from "@isoftdata/utility-string";
4
- import Modal from "@isoftdata/svelte-modal";
5
- import TextArea from "@isoftdata/svelte-textarea";
6
- let show = false;
7
- let userAccount;
8
- let lockNotes;
9
- let textarea;
10
- export let deactivateUser = void 0;
11
- export async function open(userAccountCtx) {
12
- userAccount = klona(userAccountCtx);
13
- lockNotes = userAccount.lockNotes;
14
- show = true;
15
- await tick();
16
- textarea?.select();
17
- }
18
- const { t: translate } = getContext("i18next") || { t: defaultTranslate };
1
+ <script lang="ts">
2
+ import type { UserAccount } from './'
3
+ import type { i18n } from 'i18next'
4
+
5
+ import { getContext, tick } from 'svelte'
6
+ import { klona } from 'klona'
7
+ import { translate as defaultTranslate } from '@isoftdata/utility-string'
8
+
9
+ import Modal from '@isoftdata/svelte-modal'
10
+ import TextArea from '@isoftdata/svelte-textarea'
11
+
12
+ let show: boolean = $state(false)
13
+ let userAccount: UserAccount | undefined = $state()
14
+ let lockNotes: UserAccount['lockNotes'] = $state(null)
15
+ let textarea: HTMLTextAreaElement | undefined = $state()
16
+
17
+ interface Props {
18
+ deactivateUser?: ((user: { id: UserAccount['id']; lockNotes: UserAccount['lockNotes'] }) => void | Promise<void>) | undefined;
19
+ }
20
+
21
+ let { deactivateUser = undefined }: Props = $props();
22
+ export async function open(userAccountCtx: UserAccount) {
23
+ userAccount = klona(userAccountCtx)
24
+ lockNotes = userAccount.lockNotes
25
+ show = true
26
+ await tick()
27
+ textarea?.select()
28
+ }
29
+
30
+ const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
19
31
  </script>
20
32
 
21
33
  <Modal
@@ -25,12 +37,12 @@ const { t: translate } = getContext("i18next") || { t: defaultTranslate };
25
37
  cancelButtonText={translate('common:close', 'Close')}
26
38
  confirmButtonType="submit"
27
39
  confirmButtonFormId="deactivateUserModalForm"
28
- on:close={() => (show = false)}
40
+ close={() => (show = false)}
29
41
  bind:show
30
42
  >
31
43
  <form
32
44
  id="deactivateUserModalForm"
33
- on:submit={async e => {
45
+ onsubmit={async e => {
34
46
  e.preventDefault()
35
47
  userAccount ? await deactivateUser?.({ id: userAccount.id, lockNotes }) : undefined
36
48
  show = false
@@ -1,24 +1,12 @@
1
- import { SvelteComponent } from "svelte";
2
1
  import type { UserAccount } from './';
3
- declare const __propDef: {
4
- props: {
5
- deactivateUser?: ((user: {
6
- id: UserAccount["id"];
7
- lockNotes: UserAccount["lockNotes"];
8
- }) => void | Promise<void>) | undefined;
9
- open?: (userAccountCtx: UserAccount) => Promise<void>;
10
- };
11
- events: {
12
- [evt: string]: CustomEvent<any>;
13
- };
14
- slots: {};
15
- exports?: {} | undefined;
16
- bindings?: string | undefined;
17
- };
18
- export type DeactivateUserModalProps = typeof __propDef.props;
19
- export type DeactivateUserModalEvents = typeof __propDef.events;
20
- export type DeactivateUserModalSlots = typeof __propDef.slots;
21
- export default class DeactivateUserModal extends SvelteComponent<DeactivateUserModalProps, DeactivateUserModalEvents, DeactivateUserModalSlots> {
22
- get open(): (userAccountCtx: UserAccount) => Promise<void>;
2
+ interface Props {
3
+ deactivateUser?: ((user: {
4
+ id: UserAccount['id'];
5
+ lockNotes: UserAccount['lockNotes'];
6
+ }) => void | Promise<void>) | undefined;
23
7
  }
24
- export {};
8
+ declare const DeactivateUserModal: import("svelte").Component<Props, {
9
+ open: (userAccountCtx: UserAccount) => Promise<void>;
10
+ }, "">;
11
+ type DeactivateUserModal = ReturnType<typeof DeactivateUserModal>;
12
+ export default DeactivateUserModal;
@@ -1,33 +1,46 @@
1
- <script>import { getContext } from "svelte";
2
- import Modal from "@isoftdata/svelte-modal";
3
- import Input from "@isoftdata/svelte-input";
4
- import { translate as defaultTranslate } from "@isoftdata/utility-string";
5
- const { t: translate } = getContext("i18next") || { t: defaultTranslate };
6
- export let sendPasswordRecoveryToken = void 0;
7
- export let doSendPasswordRecoveryToken;
8
- let resetDate = void 0;
9
- let email = null;
10
- let show = false;
11
- export function open(passwordRecoveryEmail, lastResetDate) {
12
- email = passwordRecoveryEmail;
13
- resetDate = lastResetDate;
14
- show = true;
15
- }
1
+ <script lang="ts">
2
+ import type { i18n } from 'i18next'
3
+
4
+ import { getContext } from 'svelte'
5
+ import Modal from '@isoftdata/svelte-modal'
6
+ import Input from '@isoftdata/svelte-input'
7
+ import { translate as defaultTranslate } from '@isoftdata/utility-string'
8
+
9
+ const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
10
+
11
+ interface Props {
12
+ sendPasswordRecoveryToken?: ((email: string | null) => void | Promise<void>) | undefined
13
+ doSendPasswordRecoveryToken: boolean
14
+ }
15
+
16
+ let { sendPasswordRecoveryToken = undefined, doSendPasswordRecoveryToken = $bindable() }: Props = $props()
17
+ let resetDate: Date | null | undefined = $state(undefined)
18
+ let email: string | null = $state(null)
19
+ let show: boolean = $state(false)
20
+
21
+ export function open(passwordRecoveryEmail: typeof email, lastResetDate: Date | null | undefined) {
22
+ email = passwordRecoveryEmail
23
+ resetDate = lastResetDate
24
+ show = true
25
+ }
16
26
  </script>
17
27
 
18
28
  <Modal
19
29
  bind:show
20
30
  title={translate('configuration.user.accountInfo.passwordRecovery.header', 'Password Recovery')}
21
- confirmButtonText={translate('configuration.user.accountInfo.passwordManagementModal.requestResetToken', 'Request Reset Token')}
31
+ confirmButtonText={translate(
32
+ 'configuration.user.accountInfo.passwordManagementModal.requestResetToken',
33
+ 'Request Reset Token',
34
+ )}
22
35
  confirmButtonIcon="check"
23
36
  confirmButtonDisabled={!email}
24
- on:close={() => (show = false)}
37
+ close={() => (show = false)}
25
38
  confirmButtonType="submit"
26
39
  confirmButtonFormId="passwordRecoveryForm"
27
40
  >
28
41
  <form
29
42
  id="passwordRecoveryForm"
30
- on:submit={async event => {
43
+ onsubmit={async event => {
31
44
  event.preventDefault()
32
45
  try {
33
46
  doSendPasswordRecoveryToken = true
@@ -38,22 +51,31 @@ export function open(passwordRecoveryEmail, lastResetDate) {
38
51
  }
39
52
  }}
40
53
  >
41
- <div class="form-row">
54
+ <div class="row">
42
55
  <Input
43
- label={translate('configuration.user.accountInfo.passwordManagementModal.lastPasswordSetDate', 'Last Password Set Date')}
56
+ label={translate(
57
+ 'configuration.user.accountInfo.passwordManagementModal.lastPasswordSetDate',
58
+ 'Last Password Set Date',
59
+ )}
44
60
  value={resetDate?.toLocaleString() ?? ''}
45
61
  labelParentClass="col-12 col-md-6 form-group mb-0"
46
62
  readonly
47
63
  />
48
64
  <Input
49
- label={translate('configuration.user.accountInfo.passwordRecoveryModal.passwordRecoveryEmail', 'Password Recovery Email')}
65
+ label={translate(
66
+ 'configuration.user.accountInfo.passwordRecoveryModal.passwordRecoveryEmail',
67
+ 'Password Recovery Email',
68
+ )}
50
69
  value={email ?? ''}
51
70
  labelParentClass="col-12 col-md-6 form-group mb-0"
52
71
  readonly
53
72
  />
54
73
  {#if !email}
55
74
  <div class="alert alert-danger mt-3 mb-0">
56
- {translate('configuration.user.accountInfo.passwordRecovery.noRecoveryEmail', 'No recovery email has been set for this user.')}
75
+ {translate(
76
+ 'configuration.user.accountInfo.passwordRecovery.noRecoveryEmail',
77
+ 'No recovery email has been set for this user.',
78
+ )}
57
79
  </div>
58
80
  {/if}
59
81
  </div>
@@ -1,21 +1,9 @@
1
- import { SvelteComponent } from "svelte";
2
- declare const __propDef: {
3
- props: {
4
- sendPasswordRecoveryToken?: ((email: string | null) => void | Promise<void>) | undefined;
5
- doSendPasswordRecoveryToken: boolean;
6
- open?: (passwordRecoveryEmail: string | null, lastResetDate: Date | undefined) => void;
7
- };
8
- events: {
9
- [evt: string]: CustomEvent<any>;
10
- };
11
- slots: {};
12
- exports?: {} | undefined;
13
- bindings?: string | undefined;
14
- };
15
- export type PasswordRecoveryModalProps = typeof __propDef.props;
16
- export type PasswordRecoveryModalEvents = typeof __propDef.events;
17
- export type PasswordRecoveryModalSlots = typeof __propDef.slots;
18
- export default class PasswordRecoveryModal extends SvelteComponent<PasswordRecoveryModalProps, PasswordRecoveryModalEvents, PasswordRecoveryModalSlots> {
19
- get open(): (passwordRecoveryEmail: string | null, lastResetDate: Date | undefined) => void;
1
+ interface Props {
2
+ sendPasswordRecoveryToken?: ((email: string | null) => void | Promise<void>) | undefined;
3
+ doSendPasswordRecoveryToken: boolean;
20
4
  }
21
- export {};
5
+ declare const PasswordRecoveryModal: import("svelte").Component<Props, {
6
+ open: (passwordRecoveryEmail: string | null, lastResetDate: Date | null | undefined) => void;
7
+ }, "doSendPasswordRecoveryToken">;
8
+ type PasswordRecoveryModal = ReturnType<typeof PasswordRecoveryModal>;
9
+ export default PasswordRecoveryModal;
@@ -1,54 +1,78 @@
1
- <script>import { klona } from "klona";
2
- import { tick, getContext } from "svelte";
3
- import Input from "@isoftdata/svelte-input";
4
- import Modal from "@isoftdata/svelte-modal";
5
- import PasswordFields from "@isoftdata/svelte-password-fields";
6
- import { translate as defaultTranslate } from "@isoftdata/utility-string";
7
- let show = false;
8
- let currentPassword = "";
9
- let newPassword = "";
10
- let confirmPassword = "";
11
- let passwordMismatch = false;
12
- let lastResetDate = void 0;
13
- let currentPasswordInput = void 0;
14
- let newPasswordInput = void 0;
15
- let passwordRecoveryEmail = void 0;
16
- let passwordIsValid = false;
17
- export let validationRules = void 0;
18
- export let changePasswordMode = false;
19
- export let confirmPasswordSet;
20
- export async function open(userAccount) {
21
- currentPassword = "";
22
- newPassword = "";
23
- confirmPassword = "";
24
- lastResetDate = klona(userAccount?.lastPasswordResetDate);
25
- passwordRecoveryEmail = userAccount?.recoveryEmail;
26
- show = true;
27
- await tick();
28
- if (changePasswordMode) {
29
- currentPasswordInput?.focus();
30
- } else {
31
- newPasswordInput?.focus();
32
- }
33
- }
34
- const { t: translate } = getContext("i18next") || { t: defaultTranslate };
35
- $: passwordMismatch = !(newPassword && confirmPassword && newPassword === confirmPassword);
36
- $: disablePasswordChange = changePasswordMode && !currentPassword || passwordMismatch || !newPassword || !confirmPassword || !passwordIsValid;
1
+ <script lang="ts">
2
+ import type { i18n } from 'i18next'
3
+ import type { UserAccount, PasswordValidationRules } from './'
4
+
5
+ import { klona } from 'klona'
6
+ import { tick, getContext } from 'svelte'
7
+ import Input from '@isoftdata/svelte-input'
8
+ import Modal from '@isoftdata/svelte-modal'
9
+ import PasswordFields from '@isoftdata/svelte-password-fields'
10
+ import { translate as defaultTranslate } from '@isoftdata/utility-string'
11
+
12
+ let show: boolean = $state(false)
13
+ let currentPassword: string = $state('')
14
+ let newPassword: string = $state('')
15
+ let confirmPassword: string = $state('')
16
+ let lastResetDate: Date | null | undefined = $state(undefined)
17
+ let currentPasswordInput: HTMLInputElement | undefined = $state(undefined)
18
+ let newPasswordInput: HTMLInputElement | undefined = $state(undefined)
19
+ let passwordRecoveryEmail: string | null | undefined = $state(undefined)
20
+ let passwordIsValid: boolean = $state(false)
21
+
22
+ interface Props {
23
+ validationRules?: PasswordValidationRules | undefined
24
+ changePasswordMode?: boolean
25
+ confirmPasswordSet: (ctx: { currentPassword: string; newPassword: string }) => void | Promise<void>
26
+ }
27
+
28
+ let { validationRules = undefined, changePasswordMode = false, confirmPasswordSet }: Props = $props()
29
+ export async function open(userAccount?: UserAccount) {
30
+ currentPassword = ''
31
+ newPassword = ''
32
+ confirmPassword = ''
33
+ lastResetDate = klona(userAccount?.lastPasswordResetDate) // Don't mutate the given Date obj
34
+ passwordRecoveryEmail = userAccount?.recoveryEmail
35
+ show = true
36
+ await tick()
37
+
38
+ if (changePasswordMode) {
39
+ currentPasswordInput?.focus()
40
+ } else {
41
+ newPasswordInput?.focus()
42
+ }
43
+ }
44
+
45
+ const { t: translate } = getContext<i18n>('i18next') || { t: defaultTranslate }
46
+
47
+ // The passwordMismatch prop from the PasswordFields component only updates on:change,
48
+ // but we want the component consumer to be able to update their save button disabled state in real time
49
+ let passwordMismatch: boolean = $derived(!(newPassword && confirmPassword && newPassword === confirmPassword))
50
+ let disablePasswordChange: boolean = $derived(
51
+ (changePasswordMode && !currentPassword) ||
52
+ passwordMismatch ||
53
+ !newPassword ||
54
+ !confirmPassword ||
55
+ !passwordIsValid,
56
+ )
37
57
  </script>
38
58
 
39
59
  <Modal
40
60
  bind:show
41
- title={changePasswordMode ? translate('configuration.user.changePassword', 'Change Password') : translate('configuration.user.setPassword', 'Set Password')}
42
- confirmButtonText={changePasswordMode ? translate('configuration.user.changePassword', 'Change Password') : translate('configuration.user.setPassword', 'Set Password')}
61
+ title={changePasswordMode
62
+ ? translate('configuration.user.changePassword', 'Change Password')
63
+ : translate('configuration.user.setPassword', 'Set Password')}
64
+ confirmButtonText={changePasswordMode
65
+ ? translate('configuration.user.changePassword', 'Change Password')
66
+ : translate('configuration.user.setPassword', 'Set Password')}
43
67
  confirmButtonIcon="check"
44
68
  confirmButtonDisabled={disablePasswordChange}
45
- on:close={() => (show = false)}
69
+ close={() => (show = false)}
46
70
  confirmButtonType="submit"
47
71
  confirmButtonFormId="setPasswordForm"
48
72
  >
49
73
  <form
50
74
  id="setPasswordForm"
51
- on:submit={async e => {
75
+ onsubmit={async e => {
52
76
  e.preventDefault()
53
77
  if (!disablePasswordChange) {
54
78
  await confirmPasswordSet({ currentPassword, newPassword })
@@ -57,7 +81,10 @@ $: disablePasswordChange = changePasswordMode && !currentPassword || passwordMis
57
81
  }}
58
82
  >
59
83
  <Input
60
- label={translate('configuration.user.accountInfo.passwordManagementModal.lastPasswordSetDate', 'Last Password Set Date')}
84
+ label={translate(
85
+ 'configuration.user.accountInfo.passwordManagementModal.lastPasswordSetDate',
86
+ 'Last Password Set Date',
87
+ )}
61
88
  tabindex={-1}
62
89
  value={lastResetDate ? lastResetDate.toLocaleString() : ''}
63
90
  labelParentClass="form-group mb-0"
@@ -83,11 +110,17 @@ $: disablePasswordChange = changePasswordMode && !currentPassword || passwordMis
83
110
  columnClass="col-12"
84
111
  {validationRules}
85
112
  passwordLabel={translate('configuration.user.accountInfo.passwordManagementModal.newPassword', 'New Password')}
86
- confirmPasswordLabel={translate('configuration.user.accountInfo.passwordManagementModal.confirmNewPassword', 'Confirm New Password')}
113
+ confirmPasswordLabel={translate(
114
+ 'configuration.user.accountInfo.passwordManagementModal.confirmNewPassword',
115
+ 'Confirm New Password',
116
+ )}
87
117
  />
88
118
  {#if !passwordRecoveryEmail}
89
119
  <div class="alert alert-danger mt-3 mb-0">
90
- {translate('configuration.user.accountInfo.passwordRecovery.noRecoveryEmail', 'No recovery email has been set for this user.')}
120
+ {translate(
121
+ 'configuration.user.accountInfo.passwordRecovery.noRecoveryEmail',
122
+ 'No recovery email has been set for this user.',
123
+ )}
91
124
  </div>
92
125
  {/if}
93
126
  </form>
@@ -1,26 +1,14 @@
1
- import { SvelteComponent } from "svelte";
2
1
  import type { UserAccount, PasswordValidationRules } from './';
3
- declare const __propDef: {
4
- props: {
5
- validationRules?: PasswordValidationRules | undefined;
6
- changePasswordMode?: boolean;
7
- confirmPasswordSet: (ctx: {
8
- currentPassword: string;
9
- newPassword: string;
10
- }) => void | Promise<void>;
11
- open?: (userAccount?: UserAccount) => Promise<void>;
12
- };
13
- events: {
14
- [evt: string]: CustomEvent<any>;
15
- };
16
- slots: {};
17
- exports?: {} | undefined;
18
- bindings?: string | undefined;
19
- };
20
- export type PasswordSetModalProps = typeof __propDef.props;
21
- export type PasswordSetModalEvents = typeof __propDef.events;
22
- export type PasswordSetModalSlots = typeof __propDef.slots;
23
- export default class PasswordSetModal extends SvelteComponent<PasswordSetModalProps, PasswordSetModalEvents, PasswordSetModalSlots> {
24
- get open(): (userAccount?: UserAccount) => Promise<void>;
2
+ interface Props {
3
+ validationRules?: PasswordValidationRules | undefined;
4
+ changePasswordMode?: boolean;
5
+ confirmPasswordSet: (ctx: {
6
+ currentPassword: string;
7
+ newPassword: string;
8
+ }) => void | Promise<void>;
25
9
  }
26
- export {};
10
+ declare const PasswordSetModal: import("svelte").Component<Props, {
11
+ open: (userAccount?: UserAccount) => Promise<void>;
12
+ }, "">;
13
+ type PasswordSetModal = ReturnType<typeof PasswordSetModal>;
14
+ export default PasswordSetModal;