@jini-ai/ui-kit 0.0.0-stage → 0.1.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/LICENSE +201 -0
- package/README.md +150 -2
- package/dist/attrs.d.ts +18 -0
- package/dist/attrs.js +2 -0
- package/dist/confirm.d.ts +42 -0
- package/dist/confirm.js +41 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +5 -0
- package/dist/kit.spec.d.ts +491 -0
- package/dist/kit.spec.js +40 -0
- package/dist/needs.d.ts +24 -0
- package/dist/needs.js +29 -0
- package/dist/react/Kit.hooks.d.ts +39 -0
- package/dist/react/Kit.hooks.js +87 -0
- package/dist/react/KitProvider.d.ts +13 -0
- package/dist/react/KitProvider.js +15 -0
- package/dist/react/confirm/ConfirmDialog.d.ts +3 -0
- package/dist/react/confirm/ConfirmDialog.hooks.d.ts +7 -0
- package/dist/react/confirm/ConfirmDialog.hooks.js +101 -0
- package/dist/react/confirm/ConfirmDialog.js +8 -0
- package/dist/react/confirm/guard.d.ts +7 -0
- package/dist/react/confirm/guard.js +54 -0
- package/dist/react/context.d.ts +9 -0
- package/dist/react/context.js +24 -0
- package/dist/react/facade.d.ts +37 -0
- package/dist/react/facade.js +36 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.js +7 -0
- package/dist/react/kit.d.ts +35 -0
- package/dist/react/kit.js +44 -0
- package/dist/react/native/Controls.d.ts +13 -0
- package/dist/react/native/Controls.hooks.d.ts +164 -0
- package/dist/react/native/Controls.hooks.js +69 -0
- package/dist/react/native/Controls.js +49 -0
- package/dist/react/native/Dialog.d.ts +4 -0
- package/dist/react/native/Dialog.hooks.d.ts +60 -0
- package/dist/react/native/Dialog.hooks.js +56 -0
- package/dist/react/native/Dialog.js +13 -0
- package/dist/react/native/Navigation.d.ts +5 -0
- package/dist/react/native/Navigation.hooks.d.ts +96 -0
- package/dist/react/native/Navigation.hooks.js +110 -0
- package/dist/react/native/Navigation.js +15 -0
- package/dist/react/native/index.d.ts +3 -0
- package/dist/react/native/index.js +9 -0
- package/dist/react/testing/driver.d.ts +27 -0
- package/dist/react/testing/driver.js +102 -0
- package/dist/react/testing/index.d.ts +5 -0
- package/dist/react/testing/index.js +3 -0
- package/dist/react/types.d.ts +210 -0
- package/dist/react/types.js +2 -0
- package/dist/testing/scenarios.d.ts +31 -0
- package/dist/testing/scenarios.js +28 -0
- package/dist/toast.d.ts +34 -0
- package/dist/toast.js +52 -0
- package/docs/decisions/DR-001.md +32 -0
- package/docs/spec/api.spec.md +98 -0
- package/docs/spec/behavior.spec.md +49 -0
- package/docs/spec/errors.spec.md +41 -0
- package/docs/spec/state.spec.md +45 -0
- package/docs/spec/ui.spec.md +49 -0
- package/package.json +95 -3
- package/src/react/native/styles.css +4 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { ChangeEvent } from 'react';
|
|
2
|
+
import type { ButtonProps, IconButtonProps, TextFieldProps, TextAreaProps, SelectProps, CheckboxProps, ToastProps, NoticeProps, SpinnerProps, BadgeProps } from '../types.js';
|
|
3
|
+
export declare function useNativeButton(required: ButtonProps, _optional?: Record<string, never>): {
|
|
4
|
+
children: import("react").ReactNode;
|
|
5
|
+
props: {
|
|
6
|
+
id?: string;
|
|
7
|
+
'data-jini-part': string;
|
|
8
|
+
ref: import("react").Ref<HTMLButtonElement> | undefined;
|
|
9
|
+
className: string;
|
|
10
|
+
type: "button" | "submit" | "reset";
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
'aria-busy': boolean | undefined;
|
|
13
|
+
'data-jini-variant': "warning" | "danger" | "primary" | "secondary" | "ghost";
|
|
14
|
+
'data-jini-state': string;
|
|
15
|
+
onClick: () => void;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export declare function useNativeIconButton(required: IconButtonProps, _optional?: Record<string, never>): {
|
|
19
|
+
props: {
|
|
20
|
+
id?: string;
|
|
21
|
+
'data-jini-part': string;
|
|
22
|
+
'aria-label': string;
|
|
23
|
+
ref: import("react").Ref<HTMLButtonElement> | undefined;
|
|
24
|
+
className: string;
|
|
25
|
+
type: "button" | "submit" | "reset";
|
|
26
|
+
disabled: boolean;
|
|
27
|
+
'aria-busy': boolean | undefined;
|
|
28
|
+
'data-jini-variant': "warning" | "danger" | "primary" | "secondary" | "ghost";
|
|
29
|
+
'data-jini-state': string;
|
|
30
|
+
onClick: () => void;
|
|
31
|
+
};
|
|
32
|
+
children: import("react").ReactNode;
|
|
33
|
+
};
|
|
34
|
+
export declare function useNativeTextField(required: TextFieldProps, _optional?: Record<string, never>): {
|
|
35
|
+
props: {
|
|
36
|
+
id: string;
|
|
37
|
+
ref: import("react").Ref<HTMLInputElement> | undefined;
|
|
38
|
+
type: "number" | "text" | "password" | "email" | "search" | "url" | "tel";
|
|
39
|
+
value: string;
|
|
40
|
+
placeholder: string | undefined;
|
|
41
|
+
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
42
|
+
className: string;
|
|
43
|
+
disabled: boolean | undefined;
|
|
44
|
+
required: boolean | undefined;
|
|
45
|
+
'data-jini-part'?: string;
|
|
46
|
+
};
|
|
47
|
+
id: string;
|
|
48
|
+
label: string;
|
|
49
|
+
labelAttrs: {
|
|
50
|
+
'data-jini-part': string;
|
|
51
|
+
className: string;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
export declare function useNativeTextArea(required: TextAreaProps, _optional?: Record<string, never>): {
|
|
55
|
+
props: {
|
|
56
|
+
className: string;
|
|
57
|
+
id: string;
|
|
58
|
+
ref: import("react").Ref<HTMLTextAreaElement> | undefined;
|
|
59
|
+
value: string;
|
|
60
|
+
rows: number;
|
|
61
|
+
placeholder: string | undefined;
|
|
62
|
+
onChange: (event: ChangeEvent<HTMLTextAreaElement>) => void;
|
|
63
|
+
disabled: boolean | undefined;
|
|
64
|
+
required: boolean | undefined;
|
|
65
|
+
'data-jini-part'?: string;
|
|
66
|
+
};
|
|
67
|
+
id: string;
|
|
68
|
+
label: string;
|
|
69
|
+
labelAttrs: {
|
|
70
|
+
'data-jini-part': string;
|
|
71
|
+
className: string;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
export declare function useNativeSelect(required: SelectProps, _optional?: Record<string, never>): {
|
|
75
|
+
options: import("react").DetailedReactHTMLElement<{
|
|
76
|
+
className: string;
|
|
77
|
+
key: string;
|
|
78
|
+
value: string;
|
|
79
|
+
disabled: boolean | undefined;
|
|
80
|
+
}, HTMLElement>[];
|
|
81
|
+
props: {
|
|
82
|
+
className: string;
|
|
83
|
+
id: string;
|
|
84
|
+
ref: import("react").Ref<HTMLSelectElement> | undefined;
|
|
85
|
+
value: string;
|
|
86
|
+
onChange: (event: ChangeEvent<HTMLSelectElement>) => void;
|
|
87
|
+
disabled: boolean | undefined;
|
|
88
|
+
required: boolean | undefined;
|
|
89
|
+
'data-jini-part'?: string;
|
|
90
|
+
};
|
|
91
|
+
id: string;
|
|
92
|
+
label: string;
|
|
93
|
+
labelAttrs: {
|
|
94
|
+
'data-jini-part': string;
|
|
95
|
+
className: string;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
export declare function useNativeCheckbox(required: CheckboxProps, optional?: {
|
|
99
|
+
role?: 'switch';
|
|
100
|
+
}): {
|
|
101
|
+
label: string;
|
|
102
|
+
id: string;
|
|
103
|
+
props: {
|
|
104
|
+
id: string;
|
|
105
|
+
ref: import("react").Ref<HTMLInputElement> | undefined;
|
|
106
|
+
className: string;
|
|
107
|
+
type: string;
|
|
108
|
+
role: "switch" | undefined;
|
|
109
|
+
checked: boolean;
|
|
110
|
+
disabled: boolean | undefined;
|
|
111
|
+
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
112
|
+
'data-jini-part'?: string;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
export declare function useNativeToast(required: ToastProps, _optional?: Record<string, never>): {
|
|
116
|
+
props: {
|
|
117
|
+
className: string;
|
|
118
|
+
role: string;
|
|
119
|
+
tabIndex: number;
|
|
120
|
+
'data-jini-variant': "warning" | "danger" | "info" | "success";
|
|
121
|
+
id?: string;
|
|
122
|
+
'data-jini-part': string;
|
|
123
|
+
};
|
|
124
|
+
message: string;
|
|
125
|
+
dismiss: import("react").DetailedReactHTMLElement<{
|
|
126
|
+
type: string;
|
|
127
|
+
className: string;
|
|
128
|
+
'data-jini-part': string;
|
|
129
|
+
'aria-label': string;
|
|
130
|
+
onClick: () => void | undefined;
|
|
131
|
+
}, HTMLElement> | null;
|
|
132
|
+
};
|
|
133
|
+
export declare function useNativeNotice(required: NoticeProps, _optional?: Record<string, never>): {
|
|
134
|
+
children: import("react").ReactNode;
|
|
135
|
+
props: {
|
|
136
|
+
className: string;
|
|
137
|
+
tabIndex: number;
|
|
138
|
+
role: string;
|
|
139
|
+
'data-jini-variant': "warning" | "danger" | "info" | "success";
|
|
140
|
+
id?: string;
|
|
141
|
+
'data-jini-part': string;
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
export declare function useNativeSpinner(required: SpinnerProps, _optional?: Record<string, never>): {
|
|
145
|
+
props: {
|
|
146
|
+
className: string;
|
|
147
|
+
id?: string;
|
|
148
|
+
'data-jini-part': string;
|
|
149
|
+
role: string;
|
|
150
|
+
'aria-label': string;
|
|
151
|
+
tabIndex: number;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
export declare function useNativeBadge(required: BadgeProps, _optional?: Record<string, never>): {
|
|
155
|
+
children: import("react").ReactNode;
|
|
156
|
+
props: {
|
|
157
|
+
className: string;
|
|
158
|
+
tabIndex: number;
|
|
159
|
+
'data-jini-variant': "warning" | "danger" | "info" | "success";
|
|
160
|
+
id?: string;
|
|
161
|
+
'data-jini-part': string;
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=Controls.hooks.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createElement, useId } from 'react';
|
|
2
|
+
export function useNativeButton(required, _optional = {}) {
|
|
3
|
+
return { children: required.children, props: { 'data-jini-part': 'kit.button', ref: required.ref, className: `jini-btn jini-btn-${required.variant ?? 'primary'} ${required.className ?? ''}`.trim(),
|
|
4
|
+
type: required.type ?? 'button', disabled: !!required.disabled || !!required.pending,
|
|
5
|
+
'aria-busy': required.pending, 'data-jini-variant': required.variant ?? 'primary',
|
|
6
|
+
'data-jini-state': required.pending ? 'pending' : required.disabled ? 'disabled' : 'ready',
|
|
7
|
+
onClick: () => { if (!required.disabled && !required.pending)
|
|
8
|
+
required.onPress?.({}); },
|
|
9
|
+
// Opaque metadata wins over generated metadata; behavioral props cannot be supplied through attrs.
|
|
10
|
+
...required.attrs } };
|
|
11
|
+
}
|
|
12
|
+
export function useNativeIconButton(required, _optional = {}) {
|
|
13
|
+
const vm = useNativeButton(required);
|
|
14
|
+
return { ...vm, props: { ...vm.props, 'data-jini-part': 'kit.icon-button', 'aria-label': required.label, ...required.attrs } };
|
|
15
|
+
}
|
|
16
|
+
function useField(required) {
|
|
17
|
+
const generated = useId();
|
|
18
|
+
return { id: required.attrs?.id ?? generated, label: required.label, labelAttrs: { 'data-jini-part': 'kit.field-label', className: 'jini-field' },
|
|
19
|
+
props: { ...required.attrs, className: `jini-input ${required.className ?? ''}`.trim(), disabled: required.disabled, required: required.required } };
|
|
20
|
+
}
|
|
21
|
+
export function useNativeTextField(required, _optional = {}) {
|
|
22
|
+
const vm = useField(required);
|
|
23
|
+
return { ...vm, props: { ...vm.props, id: vm.id, ref: required.ref, type: required.type ?? 'text', value: required.value,
|
|
24
|
+
placeholder: required.placeholder, onChange: (event) => {
|
|
25
|
+
if (!required.disabled)
|
|
26
|
+
required.onValueChange({ value: event.currentTarget.value });
|
|
27
|
+
} } };
|
|
28
|
+
}
|
|
29
|
+
export function useNativeTextArea(required, _optional = {}) {
|
|
30
|
+
const vm = useField(required);
|
|
31
|
+
return { ...vm, props: { ...vm.props, className: `jini-textarea ${required.className ?? ''}`.trim(), id: vm.id, ref: required.ref, value: required.value, rows: required.rows ?? 3,
|
|
32
|
+
placeholder: required.placeholder, onChange: (event) => {
|
|
33
|
+
if (!required.disabled)
|
|
34
|
+
required.onValueChange({ value: event.currentTarget.value });
|
|
35
|
+
} } };
|
|
36
|
+
}
|
|
37
|
+
export function useNativeSelect(required, _optional = {}) {
|
|
38
|
+
const vm = useField(required);
|
|
39
|
+
// Keep a real HTMLSelectElement: the host agent driver can set an option without library-specific knowledge.
|
|
40
|
+
const options = required.options.map(option => createElement('option', { className: 'jini-select-option', key: option.value, value: option.value, disabled: option.disabled }, option.label));
|
|
41
|
+
return { ...vm, options, props: { ...vm.props, className: `jini-select ${required.className ?? ''}`.trim(), id: vm.id, ref: required.ref, value: required.value,
|
|
42
|
+
onChange: (event) => { if (!required.disabled)
|
|
43
|
+
required.onValueChange({ value: event.currentTarget.value }); } } };
|
|
44
|
+
}
|
|
45
|
+
export function useNativeCheckbox(required, optional = {}) {
|
|
46
|
+
const generated = useId();
|
|
47
|
+
return { label: required.label, id: required.attrs?.id ?? generated,
|
|
48
|
+
props: { ...required.attrs, id: required.attrs?.id ?? generated, ref: required.ref, className: `jini-checkbox ${required.className ?? ''}`.trim(),
|
|
49
|
+
type: 'checkbox', role: optional.role, checked: required.checked, disabled: required.disabled,
|
|
50
|
+
onChange: (event) => { if (!required.disabled)
|
|
51
|
+
required.onCheckedChange({ checked: event.currentTarget.checked }); } } };
|
|
52
|
+
}
|
|
53
|
+
export function useNativeToast(required, _optional = {}) {
|
|
54
|
+
return { props: { 'data-jini-part': 'kit.toast', ...required.attrs, className: `jini-toast ${required.className ?? ''}`.trim(),
|
|
55
|
+
role: required.tone === 'danger' ? 'alert' : 'status', tabIndex: -1, 'data-jini-variant': required.tone ?? 'info' },
|
|
56
|
+
message: required.message, dismiss: required.onDismiss ? createElement('button', { type: 'button', className: 'jini-btn jini-btn-ghost jini-toast-dismiss', 'data-jini-part': 'kit.toast-dismiss',
|
|
57
|
+
'aria-label': required.dismissLabel ?? 'Dismiss notification', onClick: () => required.onDismiss?.({}) }, '×') : null };
|
|
58
|
+
}
|
|
59
|
+
export function useNativeNotice(required, _optional = {}) {
|
|
60
|
+
return { children: required.children, props: { 'data-jini-part': 'kit.notice', ...required.attrs, className: `jini-notice jini-notice-${required.tone === 'danger' ? 'error' : required.tone ?? 'info'} ${required.className ?? ''}`.trim(), tabIndex: -1,
|
|
61
|
+
role: required.tone === 'danger' ? 'alert' : 'status', 'data-jini-variant': required.tone ?? 'info' } };
|
|
62
|
+
}
|
|
63
|
+
export function useNativeSpinner(required, _optional = {}) {
|
|
64
|
+
return { props: { 'data-jini-part': 'kit.spinner', role: 'status', 'aria-label': required.label ?? 'Loading', tabIndex: -1, ...required.attrs, className: `jini-spinner ${required.className ?? ''}`.trim() } };
|
|
65
|
+
}
|
|
66
|
+
export function useNativeBadge(required, _optional = {}) {
|
|
67
|
+
return { children: required.children, props: { 'data-jini-part': 'kit.badge', ...required.attrs, className: `jini-pill jini-pill-${required.tone ?? 'info'} ${required.className ?? ''}`.trim(), tabIndex: -1, 'data-jini-variant': required.tone ?? 'info' } };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=Controls.hooks.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { useNativeButton, useNativeIconButton, useNativeTextField, useNativeTextArea, useNativeSelect, useNativeCheckbox, useNativeToast, useNativeNotice, useNativeSpinner, useNativeBadge } from './Controls.hooks.js';
|
|
4
|
+
// React's forwardRef callback is the framework-mandated positional exception; exported factories use two objects.
|
|
5
|
+
export const NativeButton = forwardRef(function NativeButton(props, ref) {
|
|
6
|
+
const vm = useNativeButton({ ...props, ref });
|
|
7
|
+
return _jsx("button", { ...vm.props, children: vm.children });
|
|
8
|
+
});
|
|
9
|
+
export const NativeIconButton = forwardRef(function NativeIconButton(props, ref) {
|
|
10
|
+
const vm = useNativeIconButton({ ...props, ref });
|
|
11
|
+
return _jsx("button", { ...vm.props, children: vm.children });
|
|
12
|
+
});
|
|
13
|
+
export const NativeTextField = forwardRef(function NativeTextField(props, ref) {
|
|
14
|
+
const vm = useNativeTextField({ ...props, ref });
|
|
15
|
+
return _jsxs("label", { htmlFor: vm.id, ...vm.labelAttrs, children: [_jsx("span", { className: "jini-field-label", children: vm.label }), _jsx("input", { ...vm.props })] });
|
|
16
|
+
});
|
|
17
|
+
export const NativeTextArea = forwardRef(function NativeTextArea(props, ref) {
|
|
18
|
+
const vm = useNativeTextArea({ ...props, ref });
|
|
19
|
+
return _jsxs("label", { htmlFor: vm.id, ...vm.labelAttrs, children: [_jsx("span", { className: "jini-field-label", children: vm.label }), _jsx("textarea", { ...vm.props })] });
|
|
20
|
+
});
|
|
21
|
+
export const NativeSelect = forwardRef(function NativeSelect(props, ref) {
|
|
22
|
+
const vm = useNativeSelect({ ...props, ref });
|
|
23
|
+
return _jsxs("label", { htmlFor: vm.id, ...vm.labelAttrs, children: [_jsx("span", { className: "jini-field-label", children: vm.label }), _jsx("select", { ...vm.props, children: vm.options })] });
|
|
24
|
+
});
|
|
25
|
+
export const NativeCheckbox = forwardRef(function NativeCheckbox(props, ref) {
|
|
26
|
+
const vm = useNativeCheckbox({ ...props, ref });
|
|
27
|
+
return _jsxs("label", { className: "jini-checkbox-field", htmlFor: vm.id, children: [_jsx("input", { ...vm.props }), vm.label] });
|
|
28
|
+
});
|
|
29
|
+
export const NativeSwitch = forwardRef(function NativeSwitch(props, ref) {
|
|
30
|
+
const vm = useNativeCheckbox({ ...props, ref }, { role: 'switch' });
|
|
31
|
+
return _jsxs("label", { className: "jini-checkbox-field", htmlFor: vm.id, children: [_jsx("input", { ...vm.props }), vm.label] });
|
|
32
|
+
});
|
|
33
|
+
export function NativeToast(props, _optional = {}) {
|
|
34
|
+
const vm = useNativeToast(props);
|
|
35
|
+
return _jsxs("div", { ...vm.props, children: [vm.message, vm.dismiss] });
|
|
36
|
+
}
|
|
37
|
+
export function NativeNotice(props, _optional = {}) {
|
|
38
|
+
const vm = useNativeNotice(props);
|
|
39
|
+
return _jsx("div", { ...vm.props, children: vm.children });
|
|
40
|
+
}
|
|
41
|
+
export function NativeSpinner(props, _optional = {}) {
|
|
42
|
+
const vm = useNativeSpinner(props);
|
|
43
|
+
return _jsx("span", { ...vm.props, children: "\u25CC" });
|
|
44
|
+
}
|
|
45
|
+
export function NativeBadge(props, _optional = {}) {
|
|
46
|
+
const vm = useNativeBadge(props);
|
|
47
|
+
return _jsx("span", { ...vm.props, children: vm.children });
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=Controls.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DialogProps, ConfirmViewProps } from '../types.js';
|
|
2
|
+
export declare const NativeDialog: import("react").ForwardRefExoticComponent<Omit<DialogProps, "ref"> & import("react").RefAttributes<HTMLDialogElement>>;
|
|
3
|
+
export declare function NativeConfirmDialog(props: ConfirmViewProps, _optional?: Record<string, never>): import("react").JSX.Element;
|
|
4
|
+
//# sourceMappingURL=Dialog.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { MouseEvent, SyntheticEvent } from 'react';
|
|
2
|
+
import type { DialogProps, ConfirmViewProps } from '../types.js';
|
|
3
|
+
export declare function useNativeDialog(required: DialogProps, optional?: {
|
|
4
|
+
initialFocus?: () => void;
|
|
5
|
+
}): {
|
|
6
|
+
title: string;
|
|
7
|
+
children: import("react").ReactNode;
|
|
8
|
+
titleId: string;
|
|
9
|
+
closeLabel: string;
|
|
10
|
+
closeProps: {
|
|
11
|
+
className: string;
|
|
12
|
+
variant: "secondary";
|
|
13
|
+
attrs: {
|
|
14
|
+
'data-jini-part': string;
|
|
15
|
+
};
|
|
16
|
+
disabled: boolean;
|
|
17
|
+
onPress: () => void;
|
|
18
|
+
};
|
|
19
|
+
props: {
|
|
20
|
+
className: string;
|
|
21
|
+
ref: import("react").RefObject<HTMLDialogElement | null>;
|
|
22
|
+
tabIndex: number;
|
|
23
|
+
'aria-busy': boolean | undefined;
|
|
24
|
+
onCancel: (event: SyntheticEvent<HTMLDialogElement>) => void;
|
|
25
|
+
onClick: (event: MouseEvent<HTMLDialogElement>) => void;
|
|
26
|
+
id?: string;
|
|
27
|
+
'data-jini-part': string;
|
|
28
|
+
'aria-labelledby': string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export declare function useNativeConfirm(required: ConfirmViewProps, _optional?: Record<string, never>): {
|
|
32
|
+
controller: import("../types.js").ConfirmController;
|
|
33
|
+
Button: import("react").ForwardRefExoticComponent<Omit<import("../types.js").ButtonProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
|
|
34
|
+
props: {
|
|
35
|
+
className: string;
|
|
36
|
+
role: string;
|
|
37
|
+
id?: string;
|
|
38
|
+
'data-jini-part': string;
|
|
39
|
+
'aria-labelledby': string;
|
|
40
|
+
ref: import("react").RefObject<HTMLDialogElement | null>;
|
|
41
|
+
tabIndex: number;
|
|
42
|
+
'aria-busy': boolean | undefined;
|
|
43
|
+
onCancel: (event: SyntheticEvent<HTMLDialogElement>) => void;
|
|
44
|
+
onClick: (event: MouseEvent<HTMLDialogElement>) => void;
|
|
45
|
+
};
|
|
46
|
+
title: string;
|
|
47
|
+
children: import("react").ReactNode;
|
|
48
|
+
titleId: string;
|
|
49
|
+
closeLabel: string;
|
|
50
|
+
closeProps: {
|
|
51
|
+
className: string;
|
|
52
|
+
variant: "secondary";
|
|
53
|
+
attrs: {
|
|
54
|
+
'data-jini-part': string;
|
|
55
|
+
};
|
|
56
|
+
disabled: boolean;
|
|
57
|
+
onPress: () => void;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=Dialog.hooks.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useId, useImperativeHandle, useLayoutEffect, useRef } from 'react';
|
|
2
|
+
import { Button } from '../facade.js';
|
|
3
|
+
import { NativeButton } from './Controls.js';
|
|
4
|
+
export function useNativeDialog(required, optional = {}) {
|
|
5
|
+
const dialogRef = useRef(null), opener = useRef(null), titleId = useId();
|
|
6
|
+
useImperativeHandle(required.ref, () => dialogRef.current, []);
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
const dialog = dialogRef.current;
|
|
9
|
+
if (!dialog)
|
|
10
|
+
return;
|
|
11
|
+
if (!required.open) {
|
|
12
|
+
if (opener.current instanceof HTMLElement && opener.current.isConnected)
|
|
13
|
+
opener.current.focus({ preventScroll: true });
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
opener.current = document.activeElement;
|
|
17
|
+
if (typeof dialog.showModal === 'function') {
|
|
18
|
+
if (!dialog.open)
|
|
19
|
+
dialog.showModal();
|
|
20
|
+
}
|
|
21
|
+
else
|
|
22
|
+
dialog.setAttribute('open', '');
|
|
23
|
+
if (optional.initialFocus)
|
|
24
|
+
optional.initialFocus();
|
|
25
|
+
else
|
|
26
|
+
(dialog.querySelector('[data-jini-autofocus]') ?? dialog.querySelector('[data-jini-part="kit.dialog-close"]') ?? dialog).focus({ preventScroll: true });
|
|
27
|
+
dialog.scrollTop = 0;
|
|
28
|
+
return () => {
|
|
29
|
+
if (typeof dialog.close === 'function') {
|
|
30
|
+
if (dialog.open)
|
|
31
|
+
dialog.close();
|
|
32
|
+
}
|
|
33
|
+
else
|
|
34
|
+
dialog.removeAttribute('open');
|
|
35
|
+
if (opener.current instanceof HTMLElement && opener.current.isConnected)
|
|
36
|
+
opener.current.focus({ preventScroll: true });
|
|
37
|
+
};
|
|
38
|
+
// Open is the transition boundary: editing content must not recapture the original trigger.
|
|
39
|
+
}, [required.open]);
|
|
40
|
+
return { title: required.title, children: required.children, titleId, closeLabel: required.closeLabel ?? 'Close',
|
|
41
|
+
closeProps: { className: 'jini-dialog-close', variant: 'secondary', attrs: { 'data-jini-part': 'kit.dialog-close' }, disabled: !!required.pending, onPress: () => { if (!required.pending)
|
|
42
|
+
required.onClose({}); } },
|
|
43
|
+
props: { 'aria-labelledby': titleId, 'data-jini-part': 'kit.dialog', ...required.attrs, className: `jini-dialog ${required.className ?? ''}`.trim(), ref: dialogRef, tabIndex: -1,
|
|
44
|
+
'aria-busy': required.pending,
|
|
45
|
+
onCancel: (event) => { event.preventDefault(); if (!required.pending)
|
|
46
|
+
required.onClose({}); },
|
|
47
|
+
onClick: (event) => { if (event.target === event.currentTarget && !required.pending)
|
|
48
|
+
required.onClose({}); } } };
|
|
49
|
+
}
|
|
50
|
+
export function useNativeConfirm(required, _optional = {}) {
|
|
51
|
+
const c = required.controller;
|
|
52
|
+
const vm = useNativeDialog({ open: c.open, title: c.title, pending: c.pending, onClose: () => c.requestDismiss({ reason: 'close' }) }, { initialFocus: () => c.focusInitial({}) });
|
|
53
|
+
return { ...vm, controller: c, Button: c.nativeActions ? NativeButton : Button,
|
|
54
|
+
props: { ...vm.props, ...c.frameAttrs, className: `jini-dialog jini-confirm-dialog ${c.className ?? ''}`.trim(), role: 'alertdialog' } };
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=Dialog.hooks.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { useNativeDialog, useNativeConfirm } from './Dialog.hooks.js';
|
|
4
|
+
import { Button } from '../facade.js';
|
|
5
|
+
export const NativeDialog = forwardRef(function NativeDialog(props, ref) {
|
|
6
|
+
const vm = useNativeDialog({ ...props, ref });
|
|
7
|
+
return _jsxs("dialog", { ...vm.props, children: [_jsx("h2", { className: "jini-dialog-title", id: vm.titleId, "data-jini-part": "kit.dialog-title", children: vm.title }), vm.children, _jsx(Button, { ...vm.closeProps, children: vm.closeLabel })] });
|
|
8
|
+
});
|
|
9
|
+
export function NativeConfirmDialog(props, _optional = {}) {
|
|
10
|
+
const vm = useNativeConfirm(props);
|
|
11
|
+
return _jsxs("dialog", { ...vm.props, children: [_jsx("h2", { className: "jini-confirm-dialog-title", ...vm.controller.titleAttrs, children: vm.controller.title }), _jsxs("div", { className: "jini-confirm-dialog-body", "data-jini-part": "confirm.body", children: [vm.controller.body, _jsx("p", { className: "jini-confirm-dialog-consequence", ...vm.controller.consequenceAttrs, children: vm.controller.consequence }), vm.controller.error] }), _jsxs("div", { className: "jini-confirm-dialog-actions", "data-jini-part": "confirm.actions", children: [_jsx(vm.Button, { ...vm.controller.cancel }), _jsx(vm.Button, { ...vm.controller.confirm })] })] });
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=Dialog.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TabsProps, MenuProps, TooltipProps } from '../types.js';
|
|
2
|
+
export declare function NativeTabs(props: TabsProps, _optional?: Record<string, never>): import("react").JSX.Element;
|
|
3
|
+
export declare function NativeMenu(props: MenuProps, _optional?: Record<string, never>): import("react").JSX.Element;
|
|
4
|
+
export declare function NativeTooltip(props: TooltipProps, _optional?: Record<string, never>): import("react").JSX.Element;
|
|
5
|
+
//# sourceMappingURL=Navigation.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { KeyboardEvent } from 'react';
|
|
2
|
+
import type { TabsProps, MenuProps, TooltipProps } from '../types.js';
|
|
3
|
+
export declare function useNativeTabs(required: TabsProps, _optional?: Record<string, never>): {
|
|
4
|
+
root: import("react").RefObject<HTMLDivElement | null>;
|
|
5
|
+
className: string;
|
|
6
|
+
label: string;
|
|
7
|
+
tabs: import("react").DetailedReactHTMLElement<{
|
|
8
|
+
'data-tab-id': string;
|
|
9
|
+
'aria-controls': string;
|
|
10
|
+
'aria-selected': boolean;
|
|
11
|
+
disabled: boolean | undefined;
|
|
12
|
+
tabIndex: number;
|
|
13
|
+
onClick: () => void;
|
|
14
|
+
onKeyDown: (event: KeyboardEvent<HTMLButtonElement>) => void;
|
|
15
|
+
id: string;
|
|
16
|
+
'data-jini-part': string;
|
|
17
|
+
key: string;
|
|
18
|
+
type: string;
|
|
19
|
+
role: "tab";
|
|
20
|
+
className: string;
|
|
21
|
+
}, HTMLElement>[];
|
|
22
|
+
panels: import("react").DetailedReactHTMLElement<{
|
|
23
|
+
key: string;
|
|
24
|
+
role: "tabpanel";
|
|
25
|
+
tabIndex: number;
|
|
26
|
+
'data-jini-part': string;
|
|
27
|
+
className: string;
|
|
28
|
+
id: string;
|
|
29
|
+
'aria-labelledby': string;
|
|
30
|
+
hidden: boolean;
|
|
31
|
+
}, HTMLElement>[];
|
|
32
|
+
};
|
|
33
|
+
export declare function useNativeMenu(required: MenuProps, _optional?: Record<string, never>): {
|
|
34
|
+
label: string;
|
|
35
|
+
items: import("react").DetailedReactHTMLElement<{
|
|
36
|
+
className: string;
|
|
37
|
+
disabled: boolean | undefined;
|
|
38
|
+
onClick: () => void;
|
|
39
|
+
onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
|
|
40
|
+
id?: string;
|
|
41
|
+
'data-jini-part': string;
|
|
42
|
+
key: string;
|
|
43
|
+
role: "menuitem";
|
|
44
|
+
type: string;
|
|
45
|
+
tabIndex: number;
|
|
46
|
+
}, HTMLElement>[];
|
|
47
|
+
popup: import("react").ReactPortal | null;
|
|
48
|
+
triggerProps: {
|
|
49
|
+
ref: import("react").RefObject<HTMLButtonElement | null>;
|
|
50
|
+
type: "button";
|
|
51
|
+
className: string;
|
|
52
|
+
disabled: boolean | undefined;
|
|
53
|
+
'aria-label': string;
|
|
54
|
+
'aria-haspopup': "menu";
|
|
55
|
+
'aria-expanded': boolean;
|
|
56
|
+
'aria-controls': string;
|
|
57
|
+
onClick: () => void;
|
|
58
|
+
onKeyDown: (event: KeyboardEvent<HTMLButtonElement>) => void;
|
|
59
|
+
id?: string;
|
|
60
|
+
'data-jini-part': string;
|
|
61
|
+
};
|
|
62
|
+
menuProps: {
|
|
63
|
+
ref: import("react").RefObject<HTMLDivElement | null>;
|
|
64
|
+
id: string;
|
|
65
|
+
role: string;
|
|
66
|
+
'aria-label': string;
|
|
67
|
+
hidden: boolean;
|
|
68
|
+
className: string;
|
|
69
|
+
'data-jini-part': string;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
export declare function useNativeTooltip(required: TooltipProps, _optional?: Record<string, never>): {
|
|
73
|
+
children: import("react").ReactNode;
|
|
74
|
+
content: import("react").ReactNode;
|
|
75
|
+
triggerProps: {
|
|
76
|
+
className: string;
|
|
77
|
+
tabIndex: number;
|
|
78
|
+
'aria-label': string | undefined;
|
|
79
|
+
'aria-describedby': string | undefined;
|
|
80
|
+
onFocus: () => void;
|
|
81
|
+
onBlur: () => void;
|
|
82
|
+
onMouseEnter: () => void;
|
|
83
|
+
onMouseLeave: () => void;
|
|
84
|
+
onKeyDown: (event: KeyboardEvent<HTMLSpanElement>) => void;
|
|
85
|
+
id?: string;
|
|
86
|
+
'data-jini-part': string;
|
|
87
|
+
};
|
|
88
|
+
tooltipProps: {
|
|
89
|
+
id: string;
|
|
90
|
+
role: string;
|
|
91
|
+
hidden: boolean;
|
|
92
|
+
className: string;
|
|
93
|
+
'data-jini-part': string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=Navigation.hooks.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { createElement, useId, useRef, useState, useEffect, useLayoutEffect } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
export function useNativeTabs(required, _optional = {}) {
|
|
4
|
+
const prefix = useId(), root = useRef(null);
|
|
5
|
+
const enabled = required.items.filter(item => !item.disabled);
|
|
6
|
+
const active = enabled.find(item => item.id === required.value) ?? enabled[0];
|
|
7
|
+
function onKeyDown(event) {
|
|
8
|
+
const index = enabled.findIndex(item => item.id === event.currentTarget.dataset.tabId);
|
|
9
|
+
const next = event.key === 'Home' ? 0 : event.key === 'End' ? enabled.length - 1 :
|
|
10
|
+
event.key === 'ArrowRight' ? (index + 1) % enabled.length : event.key === 'ArrowLeft' ? (index - 1 + enabled.length) % enabled.length : undefined;
|
|
11
|
+
if (next === undefined || !enabled[next])
|
|
12
|
+
return;
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
required.onValueChange({ value: enabled[next].id });
|
|
15
|
+
root.current?.querySelectorAll('[role="tab"]')[required.items.findIndex(item => item.id === enabled[next].id)]?.focus();
|
|
16
|
+
}
|
|
17
|
+
const tabs = required.items.map((item, index) => createElement('button', {
|
|
18
|
+
key: item.id, type: 'button', role: 'tab', id: `${prefix}-tab-${index}`, 'data-jini-part': 'kit.tab', className: 'jini-tab-bar-item',
|
|
19
|
+
...item.attrs, ...(item.id === active?.id ? required.attrs : {}),
|
|
20
|
+
'data-tab-id': item.id, 'aria-controls': `${prefix}-panel-${index}`, 'aria-selected': item.id === active?.id,
|
|
21
|
+
disabled: item.disabled, tabIndex: item.id === active?.id ? 0 : -1,
|
|
22
|
+
onClick: () => { if (!item.disabled)
|
|
23
|
+
required.onValueChange({ value: item.id }); }, onKeyDown,
|
|
24
|
+
}, item.label));
|
|
25
|
+
const panels = required.items.map((item, index) => createElement('div', { key: item.id, role: 'tabpanel', tabIndex: 0,
|
|
26
|
+
'data-jini-part': 'kit.tab-panel', className: 'jini-tab-panel', id: `${prefix}-panel-${index}`, 'aria-labelledby': `${prefix}-tab-${index}`, hidden: item.id !== active?.id }, item.content));
|
|
27
|
+
return { root, className: `jini-tabs ${required.className ?? ''}`.trim(), label: required.label, tabs, panels };
|
|
28
|
+
}
|
|
29
|
+
export function useNativeMenu(required, _optional = {}) {
|
|
30
|
+
const [open, setOpen] = useState(false), trigger = useRef(null), menu = useRef(null), id = useId();
|
|
31
|
+
const [position, setPosition] = useState({ top: 0, left: 0 });
|
|
32
|
+
useLayoutEffect(() => {
|
|
33
|
+
if (!open || !trigger.current || !menu.current)
|
|
34
|
+
return;
|
|
35
|
+
// Portals escape the preview card's overflow clipping. Follow the anchor on scroll/resize.
|
|
36
|
+
const update = () => {
|
|
37
|
+
const anchor = trigger.current.getBoundingClientRect();
|
|
38
|
+
const box = menu.current.getBoundingClientRect();
|
|
39
|
+
setPosition({ top: anchor.bottom + box.height + 4 > window.innerHeight ? Math.max(4, anchor.top - box.height - 4) : anchor.bottom + 4,
|
|
40
|
+
left: Math.max(4, Math.min(anchor.right - box.width, window.innerWidth - box.width - 4)) });
|
|
41
|
+
};
|
|
42
|
+
update();
|
|
43
|
+
window.addEventListener('resize', update);
|
|
44
|
+
window.addEventListener('scroll', update, true);
|
|
45
|
+
return () => { window.removeEventListener('resize', update); window.removeEventListener('scroll', update, true); };
|
|
46
|
+
}, [open]);
|
|
47
|
+
const enabled = required.items.filter(item => !item.disabled);
|
|
48
|
+
function close() { setOpen(false); trigger.current?.focus(); }
|
|
49
|
+
function focus(index) { menu.current?.querySelectorAll('[role="menuitem"]:not(:disabled)')[index]?.focus(); }
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!open)
|
|
52
|
+
return;
|
|
53
|
+
focus(0);
|
|
54
|
+
const dismiss = (event) => {
|
|
55
|
+
if (event.target instanceof Node && !menu.current?.contains(event.target) && !trigger.current?.contains(event.target))
|
|
56
|
+
setOpen(false);
|
|
57
|
+
};
|
|
58
|
+
document.addEventListener('pointerdown', dismiss);
|
|
59
|
+
return () => document.removeEventListener('pointerdown', dismiss);
|
|
60
|
+
}, [open]);
|
|
61
|
+
function onKeyDown(event) {
|
|
62
|
+
if (event.key === 'Escape') {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
close();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (event.key === 'Tab') {
|
|
68
|
+
setOpen(false);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const current = Array.from(menu.current?.querySelectorAll('[role="menuitem"]:not(:disabled)') ?? []).indexOf(event.target);
|
|
72
|
+
const next = event.key === 'Home' ? 0 : event.key === 'End' ? enabled.length - 1 :
|
|
73
|
+
event.key === 'ArrowDown' ? (current + 1) % enabled.length : event.key === 'ArrowUp' ? (current - 1 + enabled.length) % enabled.length : undefined;
|
|
74
|
+
if (next !== undefined) {
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
focus(next);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const items = required.items.map(item => createElement('button', { key: item.id, role: 'menuitem', type: 'button', tabIndex: -1,
|
|
80
|
+
'data-jini-part': 'kit.menu-item', ...item.attrs, className: `jini-row-menu-item ${item.attrs?.['data-jini-variant'] === 'danger' ? 'jini-btn-danger' : ''}`.trim(), disabled: item.disabled,
|
|
81
|
+
onClick: () => { if (!item.disabled) {
|
|
82
|
+
close();
|
|
83
|
+
item.onPress({});
|
|
84
|
+
} }, onKeyDown }, item.label));
|
|
85
|
+
const menuProps = { ref: menu, id, role: 'menu', 'aria-label': required.label, hidden: !open, className: 'jini-row-menu-popup', 'data-jini-part': 'kit.menu', style: { position: 'fixed', ...position } };
|
|
86
|
+
return { label: '⋮', items, popup: open ? createPortal(createElement('div', menuProps, items), document.body) : null,
|
|
87
|
+
triggerProps: { 'data-jini-part': 'kit.menu-trigger', ...required.attrs, ref: trigger, type: 'button',
|
|
88
|
+
className: `jini-row-menu-trigger ${required.className ?? ''}`.trim(), disabled: required.disabled, 'aria-label': required.label, 'aria-haspopup': 'menu', 'aria-expanded': open, 'aria-controls': id,
|
|
89
|
+
onClick: () => { if (!required.disabled)
|
|
90
|
+
setOpen(!open); }, onKeyDown: (event) => {
|
|
91
|
+
if (!required.disabled && ['ArrowDown', 'ArrowUp'].includes(event.key)) {
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
setOpen(true);
|
|
94
|
+
}
|
|
95
|
+
} },
|
|
96
|
+
menuProps: { ref: menu, id, role: 'menu', 'aria-label': required.label, hidden: !open, className: 'jini-row-menu-popup', 'data-jini-part': 'kit.menu' } };
|
|
97
|
+
}
|
|
98
|
+
export function useNativeTooltip(required, _optional = {}) {
|
|
99
|
+
const [open, setOpen] = useState(false), id = useId();
|
|
100
|
+
return { children: required.children, content: required.content,
|
|
101
|
+
triggerProps: { 'data-jini-part': 'kit.tooltip-trigger', ...required.attrs, className: `jini-tooltip-trigger ${required.className ?? ''}`.trim(), tabIndex: 0,
|
|
102
|
+
'aria-label': required.label, 'aria-describedby': open ? id : undefined,
|
|
103
|
+
onFocus: () => setOpen(true), onBlur: () => setOpen(false), onMouseEnter: () => setOpen(true), onMouseLeave: () => setOpen(false),
|
|
104
|
+
onKeyDown: (event) => { if (event.key === 'Escape') {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
setOpen(false);
|
|
107
|
+
} } },
|
|
108
|
+
tooltipProps: { id, role: 'tooltip', hidden: !open, className: 'jini-tooltip', 'data-jini-part': 'kit.tooltip' } };
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=Navigation.hooks.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useNativeTabs, useNativeMenu, useNativeTooltip } from './Navigation.hooks.js';
|
|
3
|
+
export function NativeTabs(props, _optional = {}) {
|
|
4
|
+
const vm = useNativeTabs(props);
|
|
5
|
+
return _jsxs("div", { ref: vm.root, className: vm.className, children: [_jsx("div", { className: "jini-tab-bar", role: "tablist", "aria-label": vm.label, "data-jini-part": "kit.tabs", children: vm.tabs }), vm.panels] });
|
|
6
|
+
}
|
|
7
|
+
export function NativeMenu(props, _optional = {}) {
|
|
8
|
+
const vm = useNativeMenu(props);
|
|
9
|
+
return _jsxs("div", { className: "jini-row-menu", children: [_jsx("button", { ...vm.triggerProps, children: _jsxs("svg", { className: "jini-icon", width: "16", height: "16", viewBox: "0 0 18 18", fill: "currentColor", "aria-hidden": "true", children: [_jsx("circle", { className: "jini-icon-circle", cx: "9", cy: "4.5", r: "1.5" }), _jsx("circle", { className: "jini-icon-circle", cx: "9", cy: "9", r: "1.5" }), _jsx("circle", { className: "jini-icon-circle", cx: "9", cy: "13.5", r: "1.5" })] }) }), vm.popup] });
|
|
10
|
+
}
|
|
11
|
+
export function NativeTooltip(props, _optional = {}) {
|
|
12
|
+
const vm = useNativeTooltip(props);
|
|
13
|
+
return _jsxs("span", { className: "jini-tooltip-container", children: [_jsx("span", { ...vm.triggerProps, children: vm.children }), _jsx("span", { ...vm.tooltipProps, children: vm.content })] });
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=Navigation.js.map
|