@hexdspace/react 0.0.3
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/dist/index.css +79 -0
- package/dist/index.d.mts +96 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.js +324 -0
- package/dist/index.mjs +290 -0
- package/package.json +30 -0
package/dist/index.css
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* src/feature/notifier/infra/web/react/notification-host.css */
|
|
2
|
+
@layer base {
|
|
3
|
+
.custom-toast-container {
|
|
4
|
+
--toastify-color-light: var(--surface-3);
|
|
5
|
+
--toastify-text-color-light: var(--text-1);
|
|
6
|
+
--toastify-color-progress-light: color-mix(in oklab, var(--accent-high), transparent 30%);
|
|
7
|
+
--toastify-color-dark: var(--surface-1);
|
|
8
|
+
--toastify-text-color-dark: var(--text-1);
|
|
9
|
+
--toastify-color-progress-dark: color-mix(in oklab, var(--accent-high), transparent 30%);
|
|
10
|
+
--toastify-color-progress-info: color-mix(in oklab, var(--accent), transparent 30%);
|
|
11
|
+
--toastify-color-progress-success: color-mix(in oklab, var(--success), transparent 30%);
|
|
12
|
+
--toastify-color-progress-warning: color-mix(in oklab, var(--warning), transparent 30%);
|
|
13
|
+
--toastify-color-progress-error: color-mix(in oklab, var(--danger), transparent 30%);
|
|
14
|
+
--toastify-toast-width: min(24rem, calc(100vw - 2rem));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
@supports (color-mix(in oklab, white, transparent)) {
|
|
18
|
+
@layer base {
|
|
19
|
+
.custom-toast-container {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
@layer components {
|
|
24
|
+
.toast-content {
|
|
25
|
+
padding: 0.5rem 0.75rem 1rem 0.5rem;
|
|
26
|
+
}
|
|
27
|
+
.toast-action {
|
|
28
|
+
align-self: flex-start;
|
|
29
|
+
padding: 0.35rem 0.75rem;
|
|
30
|
+
border-radius: var(--shape-radius-btn);
|
|
31
|
+
background: color-mix(in oklab, var(--secondary-3), transparent 30%);
|
|
32
|
+
color: var(--color-contrast-inverted);
|
|
33
|
+
font: inherit;
|
|
34
|
+
font-weight: 600;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
transition:
|
|
37
|
+
background-color 0.2s ease,
|
|
38
|
+
color 0.2s ease,
|
|
39
|
+
transform 0.2s ease;
|
|
40
|
+
}
|
|
41
|
+
.toast-action:hover {
|
|
42
|
+
background: var(--secondary-3);
|
|
43
|
+
color: var(--contrast-inverted);
|
|
44
|
+
}
|
|
45
|
+
.toast-action:focus-visible {
|
|
46
|
+
outline: 2px solid var(--focus);
|
|
47
|
+
outline-offset: 2px;
|
|
48
|
+
transform: translateY(-1px);
|
|
49
|
+
}
|
|
50
|
+
.toast-action:active {
|
|
51
|
+
transform: translateY(0);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
@keyframes slideIn {
|
|
55
|
+
from {
|
|
56
|
+
transform: translateY(-20px);
|
|
57
|
+
opacity: 0;
|
|
58
|
+
}
|
|
59
|
+
to {
|
|
60
|
+
transform: translateY(0);
|
|
61
|
+
opacity: 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
@keyframes slideOut {
|
|
65
|
+
from {
|
|
66
|
+
transform: translateY(0);
|
|
67
|
+
opacity: 1;
|
|
68
|
+
}
|
|
69
|
+
to {
|
|
70
|
+
transform: translateY(-20px);
|
|
71
|
+
opacity: 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
.slideIn {
|
|
75
|
+
animation: slideIn 0.35s forwards;
|
|
76
|
+
}
|
|
77
|
+
.slideOut {
|
|
78
|
+
animation: slideOut 0.25s forwards;
|
|
79
|
+
}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
+
import { QueryClient, QueryKey } from '@tanstack/react-query';
|
|
3
|
+
import { ResultOk, ResultError } from '@hexdspace/util';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
type NotificationVariant = 'success' | 'warning' | 'error' | 'info';
|
|
7
|
+
declare const DEFAULT_NOTIFICATION_CHANNEL = "app.notifications";
|
|
8
|
+
type NotificationAction = {
|
|
9
|
+
label: string;
|
|
10
|
+
onClick?: () => void | Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
interface Notification {
|
|
13
|
+
id: string;
|
|
14
|
+
title: string;
|
|
15
|
+
content?: string;
|
|
16
|
+
variant?: NotificationVariant;
|
|
17
|
+
action?: NotificationAction;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface SendNotification {
|
|
21
|
+
execute(channel: string, notification: Notification): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface NotificationListener {
|
|
25
|
+
notify: (notification: Notification) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type UnsubscribeHandler = () => void;
|
|
29
|
+
interface Subscribe {
|
|
30
|
+
execute(channel: string, observer: NotificationListener): Promise<UnsubscribeHandler>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare class NotifierController {
|
|
34
|
+
private readonly sender;
|
|
35
|
+
private readonly subscriber;
|
|
36
|
+
constructor(sender: SendNotification, subscriber: Subscribe);
|
|
37
|
+
handleSendNotification(channel: string, notification: Notification): Promise<void>;
|
|
38
|
+
handleSubscribe(channel: string, listener: NotificationListener): Promise<UnsubscribeHandler>;
|
|
39
|
+
}
|
|
40
|
+
declare const notifierController: NotifierController;
|
|
41
|
+
|
|
42
|
+
type NotificationHostProps = {
|
|
43
|
+
channel?: string;
|
|
44
|
+
isDark?: () => boolean;
|
|
45
|
+
};
|
|
46
|
+
declare const NotificationHost: React.FC<NotificationHostProps>;
|
|
47
|
+
|
|
48
|
+
declare const NotifierProvider: React.FC<{
|
|
49
|
+
children: React.ReactNode;
|
|
50
|
+
}>;
|
|
51
|
+
declare function useNotifierController(): NotifierController;
|
|
52
|
+
|
|
53
|
+
type InstructionContext = {
|
|
54
|
+
queryClient: QueryClient;
|
|
55
|
+
};
|
|
56
|
+
type CacheInstruction = {
|
|
57
|
+
type: 'invalidate';
|
|
58
|
+
queryKey: QueryKey;
|
|
59
|
+
} | {
|
|
60
|
+
type: 'update';
|
|
61
|
+
queryKey: QueryKey;
|
|
62
|
+
updater: (old: unknown) => unknown;
|
|
63
|
+
};
|
|
64
|
+
type NotificationInstruction = {
|
|
65
|
+
type: 'notify';
|
|
66
|
+
notification: Notification | ((ctx: InstructionContext) => Notification);
|
|
67
|
+
};
|
|
68
|
+
type CustomInstruction = {
|
|
69
|
+
type: 'custom';
|
|
70
|
+
run: (ctx: InstructionContext) => void | Promise<void>;
|
|
71
|
+
};
|
|
72
|
+
type Instruction = CacheInstruction | NotificationInstruction | CustomInstruction;
|
|
73
|
+
type UIOk<T> = ResultOk<T> & {
|
|
74
|
+
effects: Instruction[];
|
|
75
|
+
};
|
|
76
|
+
type UIFail = ResultError<string> & {
|
|
77
|
+
effects: Instruction[];
|
|
78
|
+
};
|
|
79
|
+
type UIResult<T> = UIOk<T> | UIFail;
|
|
80
|
+
declare const ui: {
|
|
81
|
+
success: <T>(value: T, ...effects: Instruction[]) => UIResult<T>;
|
|
82
|
+
failure: (error: string, ...extraEffects: Instruction[]) => UIResult<never>;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
type OptimisticSnapshot = {
|
|
86
|
+
rollback: () => void | Promise<void>;
|
|
87
|
+
};
|
|
88
|
+
type ResponsiveMutation<Args, Res> = {
|
|
89
|
+
mutationFn: (args: Args) => Promise<UIResult<Res>>;
|
|
90
|
+
optimistic?: (args: Args, ctx: InstructionContext) => Promise<OptimisticSnapshot | void> | OptimisticSnapshot | void;
|
|
91
|
+
onSuccess?: (data: Res) => void;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
declare function useResponsiveMutation<Args, Res>(responsiveMutation: ResponsiveMutation<Args, Res>): _tanstack_react_query.UseMutationResult<UIOk<Res>, UIFail, Args, OptimisticSnapshot | undefined>;
|
|
95
|
+
|
|
96
|
+
export { type CacheInstruction, type CustomInstruction, DEFAULT_NOTIFICATION_CHANNEL, type Instruction, type InstructionContext, type Notification, type NotificationAction, NotificationHost, type NotificationInstruction, type NotificationVariant, NotifierController, NotifierProvider, type OptimisticSnapshot, type ResponsiveMutation, type UIFail, type UIOk, type UIResult, notifierController, ui, useNotifierController, useResponsiveMutation };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
+
import { QueryClient, QueryKey } from '@tanstack/react-query';
|
|
3
|
+
import { ResultOk, ResultError } from '@hexdspace/util';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
type NotificationVariant = 'success' | 'warning' | 'error' | 'info';
|
|
7
|
+
declare const DEFAULT_NOTIFICATION_CHANNEL = "app.notifications";
|
|
8
|
+
type NotificationAction = {
|
|
9
|
+
label: string;
|
|
10
|
+
onClick?: () => void | Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
interface Notification {
|
|
13
|
+
id: string;
|
|
14
|
+
title: string;
|
|
15
|
+
content?: string;
|
|
16
|
+
variant?: NotificationVariant;
|
|
17
|
+
action?: NotificationAction;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface SendNotification {
|
|
21
|
+
execute(channel: string, notification: Notification): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface NotificationListener {
|
|
25
|
+
notify: (notification: Notification) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type UnsubscribeHandler = () => void;
|
|
29
|
+
interface Subscribe {
|
|
30
|
+
execute(channel: string, observer: NotificationListener): Promise<UnsubscribeHandler>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare class NotifierController {
|
|
34
|
+
private readonly sender;
|
|
35
|
+
private readonly subscriber;
|
|
36
|
+
constructor(sender: SendNotification, subscriber: Subscribe);
|
|
37
|
+
handleSendNotification(channel: string, notification: Notification): Promise<void>;
|
|
38
|
+
handleSubscribe(channel: string, listener: NotificationListener): Promise<UnsubscribeHandler>;
|
|
39
|
+
}
|
|
40
|
+
declare const notifierController: NotifierController;
|
|
41
|
+
|
|
42
|
+
type NotificationHostProps = {
|
|
43
|
+
channel?: string;
|
|
44
|
+
isDark?: () => boolean;
|
|
45
|
+
};
|
|
46
|
+
declare const NotificationHost: React.FC<NotificationHostProps>;
|
|
47
|
+
|
|
48
|
+
declare const NotifierProvider: React.FC<{
|
|
49
|
+
children: React.ReactNode;
|
|
50
|
+
}>;
|
|
51
|
+
declare function useNotifierController(): NotifierController;
|
|
52
|
+
|
|
53
|
+
type InstructionContext = {
|
|
54
|
+
queryClient: QueryClient;
|
|
55
|
+
};
|
|
56
|
+
type CacheInstruction = {
|
|
57
|
+
type: 'invalidate';
|
|
58
|
+
queryKey: QueryKey;
|
|
59
|
+
} | {
|
|
60
|
+
type: 'update';
|
|
61
|
+
queryKey: QueryKey;
|
|
62
|
+
updater: (old: unknown) => unknown;
|
|
63
|
+
};
|
|
64
|
+
type NotificationInstruction = {
|
|
65
|
+
type: 'notify';
|
|
66
|
+
notification: Notification | ((ctx: InstructionContext) => Notification);
|
|
67
|
+
};
|
|
68
|
+
type CustomInstruction = {
|
|
69
|
+
type: 'custom';
|
|
70
|
+
run: (ctx: InstructionContext) => void | Promise<void>;
|
|
71
|
+
};
|
|
72
|
+
type Instruction = CacheInstruction | NotificationInstruction | CustomInstruction;
|
|
73
|
+
type UIOk<T> = ResultOk<T> & {
|
|
74
|
+
effects: Instruction[];
|
|
75
|
+
};
|
|
76
|
+
type UIFail = ResultError<string> & {
|
|
77
|
+
effects: Instruction[];
|
|
78
|
+
};
|
|
79
|
+
type UIResult<T> = UIOk<T> | UIFail;
|
|
80
|
+
declare const ui: {
|
|
81
|
+
success: <T>(value: T, ...effects: Instruction[]) => UIResult<T>;
|
|
82
|
+
failure: (error: string, ...extraEffects: Instruction[]) => UIResult<never>;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
type OptimisticSnapshot = {
|
|
86
|
+
rollback: () => void | Promise<void>;
|
|
87
|
+
};
|
|
88
|
+
type ResponsiveMutation<Args, Res> = {
|
|
89
|
+
mutationFn: (args: Args) => Promise<UIResult<Res>>;
|
|
90
|
+
optimistic?: (args: Args, ctx: InstructionContext) => Promise<OptimisticSnapshot | void> | OptimisticSnapshot | void;
|
|
91
|
+
onSuccess?: (data: Res) => void;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
declare function useResponsiveMutation<Args, Res>(responsiveMutation: ResponsiveMutation<Args, Res>): _tanstack_react_query.UseMutationResult<UIOk<Res>, UIFail, Args, OptimisticSnapshot | undefined>;
|
|
95
|
+
|
|
96
|
+
export { type CacheInstruction, type CustomInstruction, DEFAULT_NOTIFICATION_CHANNEL, type Instruction, type InstructionContext, type Notification, type NotificationAction, NotificationHost, type NotificationInstruction, type NotificationVariant, NotifierController, NotifierProvider, type OptimisticSnapshot, type ResponsiveMutation, type UIFail, type UIOk, type UIResult, notifierController, ui, useNotifierController, useResponsiveMutation };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
DEFAULT_NOTIFICATION_CHANNEL: () => DEFAULT_NOTIFICATION_CHANNEL,
|
|
24
|
+
NotificationHost: () => NotificationHost,
|
|
25
|
+
NotifierController: () => NotifierController,
|
|
26
|
+
NotifierProvider: () => NotifierProvider,
|
|
27
|
+
notifierController: () => notifierController,
|
|
28
|
+
ui: () => ui,
|
|
29
|
+
useNotifierController: () => useNotifierController,
|
|
30
|
+
useResponsiveMutation: () => useResponsiveMutation
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(index_exports);
|
|
33
|
+
|
|
34
|
+
// src/util/responsive-query/ui-result.ts
|
|
35
|
+
var import_uuid = require("uuid");
|
|
36
|
+
var ui = {
|
|
37
|
+
success: (value, ...effects) => ({
|
|
38
|
+
ok: true,
|
|
39
|
+
value,
|
|
40
|
+
effects
|
|
41
|
+
}),
|
|
42
|
+
failure: (error, ...extraEffects) => ({
|
|
43
|
+
ok: false,
|
|
44
|
+
error,
|
|
45
|
+
effects: [
|
|
46
|
+
{
|
|
47
|
+
type: "notify",
|
|
48
|
+
notification: {
|
|
49
|
+
id: (0, import_uuid.v4)(),
|
|
50
|
+
title: "Error",
|
|
51
|
+
content: error,
|
|
52
|
+
variant: "error"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
...extraEffects
|
|
56
|
+
]
|
|
57
|
+
})
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// src/util/responsive-query/use-responsive-mutation.ts
|
|
61
|
+
var import_react_query = require("@tanstack/react-query");
|
|
62
|
+
|
|
63
|
+
// src/feature/notifier/entity/notification-bus.ts
|
|
64
|
+
var NotificationBus = class {
|
|
65
|
+
channels = /* @__PURE__ */ new Map();
|
|
66
|
+
subscribe(channel, listener) {
|
|
67
|
+
const listeners = this.ensureChannel(channel);
|
|
68
|
+
listeners.add(listener);
|
|
69
|
+
return () => {
|
|
70
|
+
const set = this.channels.get(channel);
|
|
71
|
+
if (!set) return;
|
|
72
|
+
set.delete(listener);
|
|
73
|
+
if (set.size === 0) {
|
|
74
|
+
this.channels.delete(channel);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
publish(channel, notification) {
|
|
79
|
+
const listeners = this.channels.get(channel);
|
|
80
|
+
if (!listeners || listeners.size === 0) return;
|
|
81
|
+
for (const listener of listeners) {
|
|
82
|
+
try {
|
|
83
|
+
listener.notify(notification);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error("Notification listener failed to handle notification", error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
ensureChannel(channel) {
|
|
90
|
+
let listeners = this.channels.get(channel);
|
|
91
|
+
if (!listeners) {
|
|
92
|
+
listeners = /* @__PURE__ */ new Set();
|
|
93
|
+
this.channels.set(channel, listeners);
|
|
94
|
+
}
|
|
95
|
+
return listeners;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// src/feature/notifier/application/use-case/send-notification-use-case.ts
|
|
100
|
+
var SendNotificationUseCase = class {
|
|
101
|
+
constructor(bus2) {
|
|
102
|
+
this.bus = bus2;
|
|
103
|
+
}
|
|
104
|
+
async execute(channel, notification) {
|
|
105
|
+
this.bus.publish(channel, notification);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// src/feature/notifier/application/use-case/subscribe-use-case.ts
|
|
110
|
+
var SubscribeUseCase = class {
|
|
111
|
+
constructor(bus2) {
|
|
112
|
+
this.bus = bus2;
|
|
113
|
+
}
|
|
114
|
+
async execute(channel, observer) {
|
|
115
|
+
return this.bus.subscribe(channel, observer);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// src/feature/notifier/infra/controllers/notifier-controller.ts
|
|
120
|
+
var NotifierController = class {
|
|
121
|
+
constructor(sender, subscriber) {
|
|
122
|
+
this.sender = sender;
|
|
123
|
+
this.subscriber = subscriber;
|
|
124
|
+
}
|
|
125
|
+
handleSendNotification(channel, notification) {
|
|
126
|
+
return this.sender.execute(channel, notification);
|
|
127
|
+
}
|
|
128
|
+
handleSubscribe(channel, listener) {
|
|
129
|
+
return this.subscriber.execute(channel, listener);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var bus = new NotificationBus();
|
|
133
|
+
var sendNotification = new SendNotificationUseCase(bus);
|
|
134
|
+
var subscribe = new SubscribeUseCase(bus);
|
|
135
|
+
var notifierController = new NotifierController(sendNotification, subscribe);
|
|
136
|
+
|
|
137
|
+
// src/feature/notifier/infra/web/react/NotificationHost.tsx
|
|
138
|
+
var import_react2 = require("react");
|
|
139
|
+
var import_react_toastify2 = require("react-toastify");
|
|
140
|
+
var import_lucide_react = require("lucide-react");
|
|
141
|
+
|
|
142
|
+
// src/feature/notifier/entity/notification.ts
|
|
143
|
+
var DEFAULT_NOTIFICATION_CHANNEL = "app.notifications";
|
|
144
|
+
|
|
145
|
+
// src/feature/notifier/infra/web/react/NotifierProvider.tsx
|
|
146
|
+
var import_react = require("react");
|
|
147
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
148
|
+
var NotifierContext = (0, import_react.createContext)(null);
|
|
149
|
+
var NotifierProvider = ({ children }) => {
|
|
150
|
+
const controller = (0, import_react.useMemo)(() => notifierController, []);
|
|
151
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NotifierContext.Provider, { value: { controller }, children });
|
|
152
|
+
};
|
|
153
|
+
function useNotifierController() {
|
|
154
|
+
const ctx = (0, import_react.useContext)(NotifierContext);
|
|
155
|
+
if (!ctx) throw new Error("useNotifierController must be used within <NotifierProvider>");
|
|
156
|
+
return ctx.controller;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/feature/notifier/infra/web/react/CustomToastTransition.tsx
|
|
160
|
+
var import_react_toastify = require("react-toastify");
|
|
161
|
+
var SlideUp = (0, import_react_toastify.cssTransition)({
|
|
162
|
+
enter: "slideIn",
|
|
163
|
+
exit: "slideOut",
|
|
164
|
+
collapseDuration: 300,
|
|
165
|
+
appendPosition: false,
|
|
166
|
+
collapse: true
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// src/feature/notifier/infra/web/react/NotificationHost.tsx
|
|
170
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
171
|
+
var NotificationHost = ({ channel = DEFAULT_NOTIFICATION_CHANNEL, isDark }) => {
|
|
172
|
+
const controller = useNotifierController();
|
|
173
|
+
(0, import_react2.useEffect)(() => {
|
|
174
|
+
let unsub;
|
|
175
|
+
let disposed = false;
|
|
176
|
+
const listener = {
|
|
177
|
+
notify(notification) {
|
|
178
|
+
renderToast(notification);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
controller.handleSubscribe(channel, listener).then((unsubHandler) => {
|
|
182
|
+
if (disposed) {
|
|
183
|
+
unsubHandler();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
unsub = unsubHandler;
|
|
187
|
+
}).catch((error) => {
|
|
188
|
+
console.error("Failed to subscribe to notifications", error);
|
|
189
|
+
});
|
|
190
|
+
return () => {
|
|
191
|
+
disposed = true;
|
|
192
|
+
if (unsub) unsub();
|
|
193
|
+
};
|
|
194
|
+
}, [channel, controller]);
|
|
195
|
+
const theme = (0, import_react2.useMemo)(() => {
|
|
196
|
+
if (!isDark) {
|
|
197
|
+
return "light";
|
|
198
|
+
}
|
|
199
|
+
return isDark() ? "dark" : "light";
|
|
200
|
+
}, []);
|
|
201
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
202
|
+
import_react_toastify2.ToastContainer,
|
|
203
|
+
{
|
|
204
|
+
position: "bottom-right",
|
|
205
|
+
transition: SlideUp,
|
|
206
|
+
newestOnTop: true,
|
|
207
|
+
draggable: true,
|
|
208
|
+
pauseOnFocusLoss: false,
|
|
209
|
+
icon: false,
|
|
210
|
+
pauseOnHover: true,
|
|
211
|
+
hideProgressBar: false,
|
|
212
|
+
className: "custom-toast-container",
|
|
213
|
+
theme
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
};
|
|
217
|
+
var DEFAULT_NOTIFICATION_VARIANT = "info";
|
|
218
|
+
var TOAST_ICON_BY_VARIANT = {
|
|
219
|
+
success: { Icon: import_lucide_react.CheckCircleIcon, color: "var(--success)" },
|
|
220
|
+
warning: { Icon: import_lucide_react.AlertTriangleIcon, color: "var(--warning)" },
|
|
221
|
+
error: { Icon: import_lucide_react.XCircleIcon, color: "var(--danger)" },
|
|
222
|
+
info: { Icon: import_lucide_react.InfoIcon, color: "var(--accent)" }
|
|
223
|
+
};
|
|
224
|
+
function renderToast(notification) {
|
|
225
|
+
const variant = notification.variant ?? DEFAULT_NOTIFICATION_VARIANT;
|
|
226
|
+
(0, import_react_toastify2.toast)(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToastContent, { notification }), {
|
|
227
|
+
type: variant,
|
|
228
|
+
toastId: notification.id,
|
|
229
|
+
icon: false
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
var ToastContent = ({ notification }) => {
|
|
233
|
+
const { title, content, action } = notification;
|
|
234
|
+
const variant = notification.variant ?? DEFAULT_NOTIFICATION_VARIANT;
|
|
235
|
+
const { Icon, color } = TOAST_ICON_BY_VARIANT[variant];
|
|
236
|
+
const handleActionClick = async () => {
|
|
237
|
+
if (!action) return;
|
|
238
|
+
try {
|
|
239
|
+
await action.onClick?.();
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error("Notification action failed", error);
|
|
242
|
+
} finally {
|
|
243
|
+
import_react_toastify2.toast.dismiss(notification.id);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "toast-content flex w-full flex-col gap-2", children: [
|
|
247
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
248
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
249
|
+
Icon,
|
|
250
|
+
{
|
|
251
|
+
className: "h-4 w-4 shrink-0",
|
|
252
|
+
style: { color },
|
|
253
|
+
"aria-hidden": "true"
|
|
254
|
+
}
|
|
255
|
+
),
|
|
256
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-semibold", children: title })
|
|
257
|
+
] }),
|
|
258
|
+
(content || action) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex w-full items-start gap-3", children: [
|
|
259
|
+
content && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "flex-1 text-sm text-muted-foreground", children: content }),
|
|
260
|
+
action && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
261
|
+
"button",
|
|
262
|
+
{
|
|
263
|
+
type: "button",
|
|
264
|
+
className: "toast-action text-sm font-medium ml-auto shrink-0 self-end",
|
|
265
|
+
onClick: handleActionClick,
|
|
266
|
+
children: action.label
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
] })
|
|
270
|
+
] });
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// src/util/responsive-query/use-responsive-mutation.ts
|
|
274
|
+
function useResponsiveMutation(responsiveMutation) {
|
|
275
|
+
const { mutationFn, optimistic, onSuccess } = responsiveMutation;
|
|
276
|
+
const qc = (0, import_react_query.useQueryClient)();
|
|
277
|
+
return (0, import_react_query.useMutation)({
|
|
278
|
+
mutationFn: async (args) => {
|
|
279
|
+
const res = await mutationFn(args);
|
|
280
|
+
if (!res.ok) throw res;
|
|
281
|
+
return res;
|
|
282
|
+
},
|
|
283
|
+
onMutate: async (args) => {
|
|
284
|
+
if (!optimistic) return void 0;
|
|
285
|
+
const snapshot = await optimistic(args, { queryClient: qc });
|
|
286
|
+
return snapshot ?? void 0;
|
|
287
|
+
},
|
|
288
|
+
onError: (err, _args, ctx) => {
|
|
289
|
+
if (ctx) void ctx.rollback();
|
|
290
|
+
executeEffects(err.effects, { queryClient: qc });
|
|
291
|
+
},
|
|
292
|
+
onSuccess: (res) => {
|
|
293
|
+
executeEffects(res.effects, { queryClient: qc });
|
|
294
|
+
onSuccess?.(res.value);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
var executeEffects = (effects, ctx) => {
|
|
299
|
+
effects.forEach((effect) => {
|
|
300
|
+
if (effect.type === "invalidate") {
|
|
301
|
+
void ctx.queryClient.invalidateQueries({ queryKey: effect.queryKey });
|
|
302
|
+
} else if (effect.type === "update") {
|
|
303
|
+
ctx.queryClient.setQueryData(effect.queryKey, effect.updater);
|
|
304
|
+
} else if (effect.type === "notify") {
|
|
305
|
+
const notification = typeof effect.notification === "function" ? effect.notification(ctx) : effect.notification;
|
|
306
|
+
void notifierController.handleSendNotification(DEFAULT_NOTIFICATION_CHANNEL, {
|
|
307
|
+
...notification
|
|
308
|
+
});
|
|
309
|
+
} else if (effect.type === "custom") {
|
|
310
|
+
void effect.run(ctx);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
};
|
|
314
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
315
|
+
0 && (module.exports = {
|
|
316
|
+
DEFAULT_NOTIFICATION_CHANNEL,
|
|
317
|
+
NotificationHost,
|
|
318
|
+
NotifierController,
|
|
319
|
+
NotifierProvider,
|
|
320
|
+
notifierController,
|
|
321
|
+
ui,
|
|
322
|
+
useNotifierController,
|
|
323
|
+
useResponsiveMutation
|
|
324
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// src/util/responsive-query/ui-result.ts
|
|
2
|
+
import { v4 as uuid } from "uuid";
|
|
3
|
+
var ui = {
|
|
4
|
+
success: (value, ...effects) => ({
|
|
5
|
+
ok: true,
|
|
6
|
+
value,
|
|
7
|
+
effects
|
|
8
|
+
}),
|
|
9
|
+
failure: (error, ...extraEffects) => ({
|
|
10
|
+
ok: false,
|
|
11
|
+
error,
|
|
12
|
+
effects: [
|
|
13
|
+
{
|
|
14
|
+
type: "notify",
|
|
15
|
+
notification: {
|
|
16
|
+
id: uuid(),
|
|
17
|
+
title: "Error",
|
|
18
|
+
content: error,
|
|
19
|
+
variant: "error"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
...extraEffects
|
|
23
|
+
]
|
|
24
|
+
})
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// src/util/responsive-query/use-responsive-mutation.ts
|
|
28
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
29
|
+
|
|
30
|
+
// src/feature/notifier/entity/notification-bus.ts
|
|
31
|
+
var NotificationBus = class {
|
|
32
|
+
channels = /* @__PURE__ */ new Map();
|
|
33
|
+
subscribe(channel, listener) {
|
|
34
|
+
const listeners = this.ensureChannel(channel);
|
|
35
|
+
listeners.add(listener);
|
|
36
|
+
return () => {
|
|
37
|
+
const set = this.channels.get(channel);
|
|
38
|
+
if (!set) return;
|
|
39
|
+
set.delete(listener);
|
|
40
|
+
if (set.size === 0) {
|
|
41
|
+
this.channels.delete(channel);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
publish(channel, notification) {
|
|
46
|
+
const listeners = this.channels.get(channel);
|
|
47
|
+
if (!listeners || listeners.size === 0) return;
|
|
48
|
+
for (const listener of listeners) {
|
|
49
|
+
try {
|
|
50
|
+
listener.notify(notification);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("Notification listener failed to handle notification", error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
ensureChannel(channel) {
|
|
57
|
+
let listeners = this.channels.get(channel);
|
|
58
|
+
if (!listeners) {
|
|
59
|
+
listeners = /* @__PURE__ */ new Set();
|
|
60
|
+
this.channels.set(channel, listeners);
|
|
61
|
+
}
|
|
62
|
+
return listeners;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/feature/notifier/application/use-case/send-notification-use-case.ts
|
|
67
|
+
var SendNotificationUseCase = class {
|
|
68
|
+
constructor(bus2) {
|
|
69
|
+
this.bus = bus2;
|
|
70
|
+
}
|
|
71
|
+
async execute(channel, notification) {
|
|
72
|
+
this.bus.publish(channel, notification);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/feature/notifier/application/use-case/subscribe-use-case.ts
|
|
77
|
+
var SubscribeUseCase = class {
|
|
78
|
+
constructor(bus2) {
|
|
79
|
+
this.bus = bus2;
|
|
80
|
+
}
|
|
81
|
+
async execute(channel, observer) {
|
|
82
|
+
return this.bus.subscribe(channel, observer);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/feature/notifier/infra/controllers/notifier-controller.ts
|
|
87
|
+
var NotifierController = class {
|
|
88
|
+
constructor(sender, subscriber) {
|
|
89
|
+
this.sender = sender;
|
|
90
|
+
this.subscriber = subscriber;
|
|
91
|
+
}
|
|
92
|
+
handleSendNotification(channel, notification) {
|
|
93
|
+
return this.sender.execute(channel, notification);
|
|
94
|
+
}
|
|
95
|
+
handleSubscribe(channel, listener) {
|
|
96
|
+
return this.subscriber.execute(channel, listener);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var bus = new NotificationBus();
|
|
100
|
+
var sendNotification = new SendNotificationUseCase(bus);
|
|
101
|
+
var subscribe = new SubscribeUseCase(bus);
|
|
102
|
+
var notifierController = new NotifierController(sendNotification, subscribe);
|
|
103
|
+
|
|
104
|
+
// src/feature/notifier/infra/web/react/NotificationHost.tsx
|
|
105
|
+
import { useEffect, useMemo as useMemo2 } from "react";
|
|
106
|
+
import { ToastContainer, toast } from "react-toastify";
|
|
107
|
+
import { AlertTriangleIcon, CheckCircleIcon, InfoIcon, XCircleIcon } from "lucide-react";
|
|
108
|
+
|
|
109
|
+
// src/feature/notifier/entity/notification.ts
|
|
110
|
+
var DEFAULT_NOTIFICATION_CHANNEL = "app.notifications";
|
|
111
|
+
|
|
112
|
+
// src/feature/notifier/infra/web/react/NotifierProvider.tsx
|
|
113
|
+
import { createContext, useContext, useMemo } from "react";
|
|
114
|
+
import { jsx } from "react/jsx-runtime";
|
|
115
|
+
var NotifierContext = createContext(null);
|
|
116
|
+
var NotifierProvider = ({ children }) => {
|
|
117
|
+
const controller = useMemo(() => notifierController, []);
|
|
118
|
+
return /* @__PURE__ */ jsx(NotifierContext.Provider, { value: { controller }, children });
|
|
119
|
+
};
|
|
120
|
+
function useNotifierController() {
|
|
121
|
+
const ctx = useContext(NotifierContext);
|
|
122
|
+
if (!ctx) throw new Error("useNotifierController must be used within <NotifierProvider>");
|
|
123
|
+
return ctx.controller;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/feature/notifier/infra/web/react/CustomToastTransition.tsx
|
|
127
|
+
import { cssTransition } from "react-toastify";
|
|
128
|
+
var SlideUp = cssTransition({
|
|
129
|
+
enter: "slideIn",
|
|
130
|
+
exit: "slideOut",
|
|
131
|
+
collapseDuration: 300,
|
|
132
|
+
appendPosition: false,
|
|
133
|
+
collapse: true
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// src/feature/notifier/infra/web/react/NotificationHost.tsx
|
|
137
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
138
|
+
var NotificationHost = ({ channel = DEFAULT_NOTIFICATION_CHANNEL, isDark }) => {
|
|
139
|
+
const controller = useNotifierController();
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
let unsub;
|
|
142
|
+
let disposed = false;
|
|
143
|
+
const listener = {
|
|
144
|
+
notify(notification) {
|
|
145
|
+
renderToast(notification);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
controller.handleSubscribe(channel, listener).then((unsubHandler) => {
|
|
149
|
+
if (disposed) {
|
|
150
|
+
unsubHandler();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
unsub = unsubHandler;
|
|
154
|
+
}).catch((error) => {
|
|
155
|
+
console.error("Failed to subscribe to notifications", error);
|
|
156
|
+
});
|
|
157
|
+
return () => {
|
|
158
|
+
disposed = true;
|
|
159
|
+
if (unsub) unsub();
|
|
160
|
+
};
|
|
161
|
+
}, [channel, controller]);
|
|
162
|
+
const theme = useMemo2(() => {
|
|
163
|
+
if (!isDark) {
|
|
164
|
+
return "light";
|
|
165
|
+
}
|
|
166
|
+
return isDark() ? "dark" : "light";
|
|
167
|
+
}, []);
|
|
168
|
+
return /* @__PURE__ */ jsx2(
|
|
169
|
+
ToastContainer,
|
|
170
|
+
{
|
|
171
|
+
position: "bottom-right",
|
|
172
|
+
transition: SlideUp,
|
|
173
|
+
newestOnTop: true,
|
|
174
|
+
draggable: true,
|
|
175
|
+
pauseOnFocusLoss: false,
|
|
176
|
+
icon: false,
|
|
177
|
+
pauseOnHover: true,
|
|
178
|
+
hideProgressBar: false,
|
|
179
|
+
className: "custom-toast-container",
|
|
180
|
+
theme
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
};
|
|
184
|
+
var DEFAULT_NOTIFICATION_VARIANT = "info";
|
|
185
|
+
var TOAST_ICON_BY_VARIANT = {
|
|
186
|
+
success: { Icon: CheckCircleIcon, color: "var(--success)" },
|
|
187
|
+
warning: { Icon: AlertTriangleIcon, color: "var(--warning)" },
|
|
188
|
+
error: { Icon: XCircleIcon, color: "var(--danger)" },
|
|
189
|
+
info: { Icon: InfoIcon, color: "var(--accent)" }
|
|
190
|
+
};
|
|
191
|
+
function renderToast(notification) {
|
|
192
|
+
const variant = notification.variant ?? DEFAULT_NOTIFICATION_VARIANT;
|
|
193
|
+
toast(/* @__PURE__ */ jsx2(ToastContent, { notification }), {
|
|
194
|
+
type: variant,
|
|
195
|
+
toastId: notification.id,
|
|
196
|
+
icon: false
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
var ToastContent = ({ notification }) => {
|
|
200
|
+
const { title, content, action } = notification;
|
|
201
|
+
const variant = notification.variant ?? DEFAULT_NOTIFICATION_VARIANT;
|
|
202
|
+
const { Icon, color } = TOAST_ICON_BY_VARIANT[variant];
|
|
203
|
+
const handleActionClick = async () => {
|
|
204
|
+
if (!action) return;
|
|
205
|
+
try {
|
|
206
|
+
await action.onClick?.();
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error("Notification action failed", error);
|
|
209
|
+
} finally {
|
|
210
|
+
toast.dismiss(notification.id);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
return /* @__PURE__ */ jsxs("div", { className: "toast-content flex w-full flex-col gap-2", children: [
|
|
214
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
215
|
+
/* @__PURE__ */ jsx2(
|
|
216
|
+
Icon,
|
|
217
|
+
{
|
|
218
|
+
className: "h-4 w-4 shrink-0",
|
|
219
|
+
style: { color },
|
|
220
|
+
"aria-hidden": "true"
|
|
221
|
+
}
|
|
222
|
+
),
|
|
223
|
+
/* @__PURE__ */ jsx2("span", { className: "font-semibold", children: title })
|
|
224
|
+
] }),
|
|
225
|
+
(content || action) && /* @__PURE__ */ jsxs("div", { className: "flex w-full items-start gap-3", children: [
|
|
226
|
+
content && /* @__PURE__ */ jsx2("span", { className: "flex-1 text-sm text-muted-foreground", children: content }),
|
|
227
|
+
action && /* @__PURE__ */ jsx2(
|
|
228
|
+
"button",
|
|
229
|
+
{
|
|
230
|
+
type: "button",
|
|
231
|
+
className: "toast-action text-sm font-medium ml-auto shrink-0 self-end",
|
|
232
|
+
onClick: handleActionClick,
|
|
233
|
+
children: action.label
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
] })
|
|
237
|
+
] });
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// src/util/responsive-query/use-responsive-mutation.ts
|
|
241
|
+
function useResponsiveMutation(responsiveMutation) {
|
|
242
|
+
const { mutationFn, optimistic, onSuccess } = responsiveMutation;
|
|
243
|
+
const qc = useQueryClient();
|
|
244
|
+
return useMutation({
|
|
245
|
+
mutationFn: async (args) => {
|
|
246
|
+
const res = await mutationFn(args);
|
|
247
|
+
if (!res.ok) throw res;
|
|
248
|
+
return res;
|
|
249
|
+
},
|
|
250
|
+
onMutate: async (args) => {
|
|
251
|
+
if (!optimistic) return void 0;
|
|
252
|
+
const snapshot = await optimistic(args, { queryClient: qc });
|
|
253
|
+
return snapshot ?? void 0;
|
|
254
|
+
},
|
|
255
|
+
onError: (err, _args, ctx) => {
|
|
256
|
+
if (ctx) void ctx.rollback();
|
|
257
|
+
executeEffects(err.effects, { queryClient: qc });
|
|
258
|
+
},
|
|
259
|
+
onSuccess: (res) => {
|
|
260
|
+
executeEffects(res.effects, { queryClient: qc });
|
|
261
|
+
onSuccess?.(res.value);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
var executeEffects = (effects, ctx) => {
|
|
266
|
+
effects.forEach((effect) => {
|
|
267
|
+
if (effect.type === "invalidate") {
|
|
268
|
+
void ctx.queryClient.invalidateQueries({ queryKey: effect.queryKey });
|
|
269
|
+
} else if (effect.type === "update") {
|
|
270
|
+
ctx.queryClient.setQueryData(effect.queryKey, effect.updater);
|
|
271
|
+
} else if (effect.type === "notify") {
|
|
272
|
+
const notification = typeof effect.notification === "function" ? effect.notification(ctx) : effect.notification;
|
|
273
|
+
void notifierController.handleSendNotification(DEFAULT_NOTIFICATION_CHANNEL, {
|
|
274
|
+
...notification
|
|
275
|
+
});
|
|
276
|
+
} else if (effect.type === "custom") {
|
|
277
|
+
void effect.run(ctx);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
export {
|
|
282
|
+
DEFAULT_NOTIFICATION_CHANNEL,
|
|
283
|
+
NotificationHost,
|
|
284
|
+
NotifierController,
|
|
285
|
+
NotifierProvider,
|
|
286
|
+
notifierController,
|
|
287
|
+
ui,
|
|
288
|
+
useNotifierController,
|
|
289
|
+
useResponsiveMutation
|
|
290
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hexdspace/react",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"module": "dist/index.mjs",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"author": "piotr.jasinski990@gmail.com",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@tanstack/react-query": "^5.90.11",
|
|
17
|
+
"lucide-react": "^0.555.0",
|
|
18
|
+
"react": "^19.2.0",
|
|
19
|
+
"react-toastify": "^11.0.5",
|
|
20
|
+
"uuid": "^13.0.0",
|
|
21
|
+
"@hexdspace/util": "0.0.1"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/react": "^19.2.7"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup src/index.ts --dts --format cjs,esm",
|
|
28
|
+
"test": "vitest"
|
|
29
|
+
}
|
|
30
|
+
}
|