adminforth 2.70.0-next.5 → 2.71.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.
@@ -7,7 +7,7 @@
7
7
  :class="{
8
8
  'cursor-default opacity-50 pointer-events-none': props.disabled,
9
9
  'active brightness-200 hover:brightness-150' : props.active,
10
- 'text-lightSecondaryContrast/70 bg-lightSecondary border-lightSecondaryContrast/30 dark:bg-darkSecondary hover:bg-lightSecondary/60 hover:border-lightSecondaryContrast/60 focus:ring-lightSecondary dark:focus:ring-darkSecondary/40 dark:text-darkSecondaryContrast dark:border-darkSecondaryContrast/40 dark:hover:bg-darkSecondary/60 dark:hover:border-white/60': currentVariant === 'secondary',
10
+ 'text-lightSecondaryContrast/70 bg-lightSecondary border-lightSecondaryContrast/30 dark:bg-darkSecondary hover:bg-lightSecondary/60 hover:border-lightSecondaryContrast/60 focus:ring-lightSecondary dark:focus:ring-darkSecondary/40 dark:text-darkSecondaryContrast dark:border-darkSecondaryContrast/40 dark:hover:bg-darkSecondary/60 dark:hover:border-white/60': props.mode === 'secondary',
11
11
  }"
12
12
  >
13
13
  <Spinner v-if="props.loader" class="w-4 h-4 text-lightButtonsText dark:text-darkButtonsText fill-lightButtonsBackground dark:fill-darkPrimary" />
@@ -17,22 +17,17 @@
17
17
 
18
18
  <script setup lang="ts">
19
19
  import { Spinner } from '@/afcl';
20
- import { computed } from 'vue';
21
20
 
22
21
  const props = withDefaults(defineProps<{
23
22
  loader?: boolean;
24
23
  disabled?: boolean;
25
24
  active?: boolean;
26
- variant?: 'primary' | 'secondary';
27
- /** @deprecated use variant instead of mode */
28
25
  mode?: 'primary' | 'secondary';
29
26
  }>(), {
30
27
  loader: false,
31
28
  disabled: false,
32
29
  active: false,
30
+ mode: 'primary'
33
31
  });
34
32
 
35
- // mode is deprecated, but we still want to support it for backward compatibility,
36
- // so we check both variant and mode props
37
- const currentVariant = computed(() => props.variant ?? props.mode ?? 'primary');
38
33
  </script>
@@ -5,7 +5,6 @@
5
5
  :closeByClickOutside="clickToCloseOutside || closeByClickOutside"
6
6
  :closeByEsc="closeByEsc || closable"
7
7
  :beforeCloseFunction="beforeCloseFunction"
8
- :beforeCancelFunction="beforeCancelFunction"
9
8
  :beforeOpenFunction="beforeOpenFunction"
10
9
  :askForCloseConfirmation="askForCloseConfirmation"
11
10
  :closeConfirmationText="closeConfirmationText"
@@ -27,7 +26,7 @@
27
26
  v-if="headerCloseButton"
28
27
  type="button"
29
28
  class="text-lightDialogCloseButton bg-transparent hover:bg-lightDialogCloseButtonHoverBackground hover:text-lightDialogCloseButtonHover rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:text-darkDialogCloseButton dark:hover:bg-darkDialogCloseButtonHoverBackground dark:hover:text-darkDialogCloseButtonHover"
30
- @click="tryToCancelModal"
29
+ @click="tryToHideModal"
31
30
  >
32
31
  <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
33
32
  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
@@ -119,15 +118,10 @@ function close() {
119
118
  modalRef.value.close();
120
119
  }
121
120
 
122
- function tryToCancelModal() {
123
- modalRef.value?.cancel();
124
- }
125
-
126
121
  defineExpose({
127
122
  open: open,
128
123
  close: close,
129
124
  tryToHideModal: tryToHideModal,
130
- tryToCancelModal: tryToCancelModal,
131
125
  })
132
126
 
133
127
  function tryToHideModal() {
@@ -177,11 +171,6 @@ interface DialogProps {
177
171
  */
178
172
  beforeOpenFunction?: (() => void | Promise<void>) | null
179
173
 
180
- /**
181
- * Function that will be called before the dialog is canceled.
182
- */
183
- beforeCancelFunction?: (() => void | Promise<void | boolean>) | null
184
-
185
174
  /**
186
175
  * Disables close on Ecs button
187
176
  *
@@ -212,21 +201,19 @@ interface DialogProps {
212
201
 
213
202
  /********** for the backward compatibility ***************/
214
203
  class Dialog implements IDialogInsideButtonClickHandler {
215
- hide: (isCancel?: boolean) => void
216
- constructor(hideFn: (isCancel?: boolean) => void) {
217
- this.hide = hideFn;
204
+ hide: () => void
205
+ constructor( hide: () => void ) {
206
+ this.hide = hide;
218
207
  }
219
208
  }
220
209
  const dialog: Ref<Dialog> = ref(
221
- new Dialog((isCancel = false) => {
222
- if (dialog.value) {
223
- if (isCancel) {
224
- modalRef.value?.cancel();
225
- } else {
226
- modalRef.value?.close();
210
+ new Dialog(
211
+ () => {
212
+ if (dialog.value) {
213
+ tryToHideModal();
227
214
  }
228
215
  }
229
- })
216
+ )
230
217
  );
231
218
  /*************************************************************/
232
219
 
@@ -70,7 +70,6 @@ interface DialogProps {
70
70
  closeByEsc?: boolean
71
71
  beforeCloseFunction?: (() => void | Promise<void | boolean>) | null
72
72
  beforeOpenFunction?: (() => void | Promise<void>) | null
73
- beforeCancelFunction?: (() => void | Promise<void | boolean>) | null
74
73
  askForCloseConfirmation?: boolean
75
74
  closeConfirmationText?: string
76
75
  removeFromDomOnClose?: boolean
@@ -83,7 +82,6 @@ const props = withDefaults(defineProps<DialogProps>(), {
83
82
  closeByEsc: true,
84
83
  beforeCloseFunction: null,
85
84
  beforeOpenFunction: null,
86
- beforeCancelFunction: null,
87
85
  askForCloseConfirmation: false,
88
86
  closeConfirmationText: 'Are you sure you want to close this dialog?',
89
87
  removeFromDomOnClose: false,
@@ -111,14 +109,6 @@ async function close() {
111
109
  isModalOpen.value = false;
112
110
  }
113
111
 
114
- async function cancel() {
115
- if (props.beforeCancelFunction) {
116
- const shouldCancel = await props.beforeCancelFunction?.();
117
- if (shouldCancel === false) return;
118
- }
119
- isModalOpen.value = false;
120
- }
121
-
122
112
  defineOptions({
123
113
  inheritAttrs: false,
124
114
  })
@@ -126,17 +116,12 @@ defineOptions({
126
116
  defineExpose({
127
117
  open: open,
128
118
  close: close,
129
- cancel: cancel,
130
119
  tryToHideModal: tryToHideModal,
131
120
  })
132
121
 
133
- function tryToHideModal(isCancelAction = false) {
134
- if (!props.askForCloseConfirmation) {
135
- if (isCancelAction) {
136
- cancel();
137
- } else {
138
- close();
139
- }
122
+ function tryToHideModal() {
123
+ if (!props.askForCloseConfirmation ) {
124
+ close();
140
125
  } else {
141
126
  showConfirmationOnClose.value = true;
142
127
  }
@@ -151,8 +136,10 @@ function toggleModal() {
151
136
  }
152
137
 
153
138
  function onEsc(event: KeyboardEvent) {
154
- if (event.key === 'Escape' && props.closeByEsc) {
155
- tryToHideModal(true);
139
+ if (event.key === 'Escape') {
140
+ if (props.closeByEsc) {
141
+ tryToHideModal();
142
+ }
156
143
  }
157
144
  }
158
145
 
@@ -167,7 +154,7 @@ onUnmounted(() => {
167
154
 
168
155
  function backdropClick(e: MouseEvent) {
169
156
  if (props.closeByClickOutside && e.target === e.currentTarget) {
170
- tryToHideModal(true);
157
+ tryToHideModal();
171
158
  }
172
159
  }
173
160
 
@@ -35,7 +35,7 @@
35
35
  @click="()=>{checkboxes = []}"
36
36
  v-if="checkboxes.length"
37
37
  data-tooltip-target="tooltip-remove-all"
38
- class="flex gap-1 items-center py-1 px-3 text-sm font-medium text-lightListViewButtonText af-button-shadow
38
+ class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-lightListViewButtonText af-button-shadow
39
39
  focus:outline-none bg-lightListViewButtonBackground rounded border border-lightListViewButtonBorder h-[2.125rem]
40
40
  hover:bg-lightListViewButtonBackgroundHover hover:text-lightListViewButtonTextHover focus:z-10 focus:ring-4
41
41
  focus:ring-lightListViewButtonFocusRing dark:focus:ring-darkListViewButtonFocusRing
@@ -125,7 +125,7 @@
125
125
  </RouterLink>
126
126
 
127
127
  <button
128
- class="af-filter-button flex gap-1 items-center py-1 h-[2.125rem] px-3 af-button-shadow text-sm font-medium
128
+ class="af-filter-button flex gap-1 items-center py-1 h-[2.125rem] px-3 me-2 af-button-shadow text-sm font-medium
129
129
  text-lightListViewButtonText transition-all focus:outline-none bg-lightListViewButtonBackground rounded border
130
130
  border-lightListViewButtonBorder hover:bg-lightListViewButtonBackgroundHover hover:text-lightListViewButtonTextHover
131
131
  focus:z-10 focus:ring-4 focus:ring-lightListViewButtonFocusRing dark:focus:ring-darkListViewButtonFocusRing
@@ -1,5 +1,13 @@
1
1
 
2
- const subscriptions: { [topic: string]: ((data: any) => void)[] } = {};
2
+ type WebsocketCallback = (data: any) => void;
3
+ type Unsubscribe = () => void;
4
+ type Subscription = {
5
+ id: number;
6
+ callback: WebsocketCallback;
7
+ };
8
+
9
+ const subscriptions: { [topic: string]: Subscription[] } = {};
10
+ let nextSubscriptionId = 1;
3
11
 
4
12
  interface ExtendedWebSocket extends WebSocket {
5
13
  connected?: boolean;
@@ -65,8 +73,8 @@ async function connect () {
65
73
  const topic = message.topic;
66
74
  const data = message.data;
67
75
  if (subscriptions[topic]) {
68
- for (const callback of subscriptions[topic]) {
69
- callback(data);
76
+ for (const subscription of subscriptions[topic]) {
77
+ subscription.callback(data);
70
78
  }
71
79
  }
72
80
  }
@@ -104,19 +112,49 @@ setInterval(() => {
104
112
  }, 10_000);
105
113
 
106
114
 
115
+ function unsubscribeSubscription(topic: string, subscriptionId: number): void {
116
+ if (!subscriptions[topic]) {
117
+ return;
118
+ }
119
+
120
+ subscriptions[topic] = subscriptions[topic].filter((subscription) => subscription.id !== subscriptionId);
121
+ if (subscriptions[topic].length > 0) {
122
+ return;
123
+ }
124
+
125
+ delete subscriptions[topic];
126
+ if (state.status === 'connected') {
127
+ doPhysicalUnsubscribe(topic);
128
+ }
129
+ }
130
+
107
131
  export default {
108
- subscribe(topic: string, callback: (data: any) => void): void {
132
+ subscribe(topic: string, callback: WebsocketCallback): Unsubscribe {
109
133
  const isFirstSubscription = !subscriptions[topic];
110
134
  if (!subscriptions[topic]) {
111
135
  subscriptions[topic] = [];
112
136
  }
113
- subscriptions[topic].push(callback);
137
+ const subscriptionId = nextSubscriptionId++;
138
+ subscriptions[topic].push({
139
+ id: subscriptionId,
140
+ callback,
141
+ });
114
142
  if (isFirstSubscription && state.status === 'connected') {
115
143
  doPhysicalSubscribe(topic);
116
144
  }
145
+
146
+ let isSubscribed = true;
147
+ return () => {
148
+ if (!isSubscribed) {
149
+ return;
150
+ }
151
+ isSubscribed = false;
152
+ unsubscribeSubscription(topic, subscriptionId);
153
+ };
117
154
  },
118
155
 
119
156
  unsubscribe(topic: string): void {
157
+ console.warn('This method is deprecated because it removes all subscriptions for the topic. Use the unsubscribe function returned by subscribe(), or use unsubscribeByPrefix() when you explicitly need to unsubscribe by topic prefix.');
120
158
  if (!subscriptions[topic]) {
121
159
  return;
122
160
  }
@@ -146,4 +184,4 @@ export default {
146
184
  });
147
185
  }
148
186
 
149
- }
187
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "2.70.0-next.5",
3
+ "version": "2.71.0",
4
4
  "description": "OpenSource Agent-Native forth-generation admin panel",
5
5
  "keywords": [
6
6
  "adminforth",