dce-reactkit 3.5.3 → 3.5.5
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/cjs/index.js +562 -142
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/AppWrapper.d.ts +5 -0
- package/dist/cjs/types/components/ToggleSwitch.d.ts +20 -0
- package/dist/cjs/types/components/Tooltip.d.ts +15 -0
- package/dist/cjs/types/index.d.ts +4 -2
- package/dist/esm/index.js +561 -144
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/AppWrapper.d.ts +5 -0
- package/dist/esm/types/components/ToggleSwitch.d.ts +20 -0
- package/dist/esm/types/components/Tooltip.d.ts +15 -0
- package/dist/esm/types/index.d.ts +4 -2
- package/dist/index.d.ts +76 -37
- package/package.json +2 -1
- package/src/components/AppWrapper.tsx +52 -34
- package/src/components/CopiableBox.tsx +5 -4
- package/src/components/ToggleSwitch.tsx +102 -0
- package/src/components/Tooltip.tsx +567 -0
- package/src/index.ts +11 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A toggle switch that toggles on or off
|
|
3
|
+
* @author Alessandra De Lucas
|
|
4
|
+
* @author Gabe Abrams
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Import React
|
|
8
|
+
import React from 'react';
|
|
9
|
+
|
|
10
|
+
// Import FontAwesome
|
|
11
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
12
|
+
import { faCircle } from '@fortawesome/free-solid-svg-icons';
|
|
13
|
+
|
|
14
|
+
// Import shared types
|
|
15
|
+
import Variant from '../types/Variant';
|
|
16
|
+
|
|
17
|
+
/*------------------------------------------------------------------------*/
|
|
18
|
+
/* -------------------------------- Types ------------------------------- */
|
|
19
|
+
/*------------------------------------------------------------------------*/
|
|
20
|
+
|
|
21
|
+
// Props definition
|
|
22
|
+
type Props = {
|
|
23
|
+
// If true, the toggle switch is in the on position
|
|
24
|
+
isOn: boolean,
|
|
25
|
+
/**
|
|
26
|
+
* A handler to call when the switch is toggled
|
|
27
|
+
* @param isOn Updated value for isOn
|
|
28
|
+
*/
|
|
29
|
+
onToggle: (isOn: boolean) => void,
|
|
30
|
+
// Unique ID for the toggle switch
|
|
31
|
+
id?: string,
|
|
32
|
+
// A description for what it means when the toggle switch is on
|
|
33
|
+
// E.g. "airplane mode is active"
|
|
34
|
+
// Description will be placed into a sentence with the structure:
|
|
35
|
+
// `If on, ${description}. Currently on. Click to turn off.`
|
|
36
|
+
description: string,
|
|
37
|
+
// Custom background variant for the "on" state (defaults to Variant.Info)
|
|
38
|
+
backgroundVariantWhenOn?: Variant,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/*------------------------------------------------------------------------*/
|
|
42
|
+
/* ------------------------------ Component ----------------------------- */
|
|
43
|
+
/*------------------------------------------------------------------------*/
|
|
44
|
+
|
|
45
|
+
const ToggleSwitch: React.FC<Props> = (props) => {
|
|
46
|
+
/*------------------------------------------------------------------------*/
|
|
47
|
+
/* -------------------------------- Setup ------------------------------- */
|
|
48
|
+
/*------------------------------------------------------------------------*/
|
|
49
|
+
|
|
50
|
+
/* -------------- Props ------------- */
|
|
51
|
+
|
|
52
|
+
// Destructure all props
|
|
53
|
+
const {
|
|
54
|
+
isOn,
|
|
55
|
+
onToggle,
|
|
56
|
+
id,
|
|
57
|
+
description,
|
|
58
|
+
backgroundVariantWhenOn = Variant.Info,
|
|
59
|
+
} = props;
|
|
60
|
+
|
|
61
|
+
/*------------------------------------------------------------------------*/
|
|
62
|
+
/* ------------------------------- Render ------------------------------- */
|
|
63
|
+
/*------------------------------------------------------------------------*/
|
|
64
|
+
|
|
65
|
+
/*----------------------------------------*/
|
|
66
|
+
/* --------------- Main UI -------------- */
|
|
67
|
+
/*----------------------------------------*/
|
|
68
|
+
|
|
69
|
+
const backgroundVariant = (
|
|
70
|
+
isOn
|
|
71
|
+
? backgroundVariantWhenOn
|
|
72
|
+
: 'secondary'
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<button
|
|
77
|
+
id={id}
|
|
78
|
+
aria-label={`If on, ${description}. Currently ${isOn ? 'on' : 'off'}. Click to turn ${isOn ? 'off' : 'on'}.`}
|
|
79
|
+
className={`alert alert-${backgroundVariant} bg-${backgroundVariant} mb-0 rounded-pill d-inline-block pt-0 pb-0 px-3`}
|
|
80
|
+
onClick={() => {
|
|
81
|
+
onToggle(!isOn);
|
|
82
|
+
}}
|
|
83
|
+
type="button"
|
|
84
|
+
>
|
|
85
|
+
<FontAwesomeIcon
|
|
86
|
+
icon={faCircle}
|
|
87
|
+
className="text-light"
|
|
88
|
+
style={{
|
|
89
|
+
transform: `translateX(${isOn ? '' : '-'}0.7rem)`,
|
|
90
|
+
transition: '0.3s transform ease-in-out',
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
</button>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/*------------------------------------------------------------------------*/
|
|
98
|
+
/* ------------------------------- Wrap Up ------------------------------ */
|
|
99
|
+
/*------------------------------------------------------------------------*/
|
|
100
|
+
|
|
101
|
+
// Export component
|
|
102
|
+
export default ToggleSwitch;
|
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple tooltip component
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import React
|
|
7
|
+
import React, { useEffect, useReducer, useRef } from 'react';
|
|
8
|
+
|
|
9
|
+
// Import FontAwesome
|
|
10
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
11
|
+
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
|
12
|
+
import { faCaretDown } from '@fortawesome/free-solid-svg-icons';
|
|
13
|
+
|
|
14
|
+
/*------------------------------------------------------------------------*/
|
|
15
|
+
/* -------------------------------- Types ------------------------------- */
|
|
16
|
+
/*------------------------------------------------------------------------*/
|
|
17
|
+
|
|
18
|
+
// Props definition
|
|
19
|
+
type Props = {
|
|
20
|
+
// Icon to show next to the text
|
|
21
|
+
icon?: IconProp,
|
|
22
|
+
// Text of the tooltip
|
|
23
|
+
text: string,
|
|
24
|
+
// Children to wrap in the tooltip
|
|
25
|
+
children: React.ReactNode,
|
|
26
|
+
// If true, tooltip is wider
|
|
27
|
+
wide?: boolean,
|
|
28
|
+
// If true, tooltip is thinner
|
|
29
|
+
thin?: boolean,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/*------------------------------------------------------------------------*/
|
|
33
|
+
/* ------------------------------ Constants ----------------------------- */
|
|
34
|
+
/*------------------------------------------------------------------------*/
|
|
35
|
+
|
|
36
|
+
// Tooltip widths
|
|
37
|
+
const TOOLTIP_WIDTH_THIN_REM = 5;
|
|
38
|
+
const TOOLTIP_WIDTH_NORMAL_REM = 7;
|
|
39
|
+
const TOOLTIP_WIDTH_WIDE_REM = 10;
|
|
40
|
+
|
|
41
|
+
// Tooltip border radius
|
|
42
|
+
const TOOLTIP_BORDER_RADIUS_REM = 0.3;
|
|
43
|
+
|
|
44
|
+
/*------------------------------------------------------------------------*/
|
|
45
|
+
/* -------------------------------- Style ------------------------------- */
|
|
46
|
+
/*------------------------------------------------------------------------*/
|
|
47
|
+
|
|
48
|
+
const style = `
|
|
49
|
+
/* Container for contents and tooltip */
|
|
50
|
+
.Tooltip-outer-container {
|
|
51
|
+
display: inline-block;
|
|
52
|
+
position: relative;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Container for just tooltip */
|
|
56
|
+
.Tooltip-tooltip-container {
|
|
57
|
+
/* Position in middle but take no space */
|
|
58
|
+
/* (so layout of page is unaffected) */
|
|
59
|
+
position: absolute;
|
|
60
|
+
display: inline-block;
|
|
61
|
+
top: 0;
|
|
62
|
+
left: 50%;
|
|
63
|
+
height: 0;
|
|
64
|
+
width: 0;
|
|
65
|
+
overflow: visible;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Tooltip */
|
|
69
|
+
.Tooltip-tooltip {
|
|
70
|
+
/* Position above item */
|
|
71
|
+
display: inline-block;
|
|
72
|
+
position: absolute;
|
|
73
|
+
top: -2.3rem;
|
|
74
|
+
height: 1.8rem;
|
|
75
|
+
z-index: 1;
|
|
76
|
+
|
|
77
|
+
/* Use standard black font */
|
|
78
|
+
font-size: 1rem;
|
|
79
|
+
color: black;
|
|
80
|
+
|
|
81
|
+
/* Non-interactive */
|
|
82
|
+
pointer-events: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Tooltip box (the rectangle surrounding the text) */
|
|
86
|
+
.Tooltip-box,
|
|
87
|
+
.Tooltip-box-shadow,
|
|
88
|
+
.Tooltip-box-highlight {
|
|
89
|
+
/* Fill all space with rectangle */
|
|
90
|
+
position: absolute;
|
|
91
|
+
left: 0;
|
|
92
|
+
top: 0;
|
|
93
|
+
display: inline-block;
|
|
94
|
+
width: 100%;
|
|
95
|
+
height: 100%;
|
|
96
|
+
|
|
97
|
+
/* Rounded border */
|
|
98
|
+
border: 0.15rem solid black;
|
|
99
|
+
border-radius: ${TOOLTIP_BORDER_RADIUS_REM}rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Tooltip highlight (the bright background to the text) */
|
|
103
|
+
.Tooltip-box-highlight {
|
|
104
|
+
/* Bootstrap warning variant background */
|
|
105
|
+
background-color: rgb(255, 193, 7);
|
|
106
|
+
border-radius: ${TOOLTIP_BORDER_RADIUS_REM}rem;
|
|
107
|
+
|
|
108
|
+
/* Place on top */
|
|
109
|
+
z-index: -1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Tooltip box (the black rectangle) */
|
|
113
|
+
.Tooltip-box {
|
|
114
|
+
/* Plain black background */
|
|
115
|
+
background-color: black;
|
|
116
|
+
|
|
117
|
+
/* Place in middle */
|
|
118
|
+
z-index: -2;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Tooltip box shadow (the white shadow beneath the box) */
|
|
122
|
+
.Tooltip-box-shadow {
|
|
123
|
+
/* White shadow */
|
|
124
|
+
box-shadow: 0 0 0.3rem white;
|
|
125
|
+
|
|
126
|
+
/* Place beneath */
|
|
127
|
+
z-index: -3;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Tooltip contents (the text and icon) */
|
|
131
|
+
.Tooltip-contents {
|
|
132
|
+
/* Center text, don't allow overflow */
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
overflow: hidden;
|
|
135
|
+
text-align: center;
|
|
136
|
+
text-overflow: ellipsis;
|
|
137
|
+
display: flex;
|
|
138
|
+
align-items: center;
|
|
139
|
+
justify-content: center;
|
|
140
|
+
|
|
141
|
+
/* Space within parent */
|
|
142
|
+
z-index: 1;
|
|
143
|
+
padding-left: 0.2rem;
|
|
144
|
+
padding-right: 0.2rem;
|
|
145
|
+
height: 100%;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Tooltip text */
|
|
149
|
+
.Tooltip-text {
|
|
150
|
+
/* Center text, add ellipsis */
|
|
151
|
+
overflow: hidden;
|
|
152
|
+
text-align: center;
|
|
153
|
+
text-overflow: ellipsis;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Classes to switch between tooltip visible/hidden */
|
|
157
|
+
.Tooltip-tooltip-visible-true {
|
|
158
|
+
opacity: 1;
|
|
159
|
+
}
|
|
160
|
+
.Tooltip-tooltip-visible-false {
|
|
161
|
+
opacity: 0;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* Classes to switch between tooltip sizes */
|
|
165
|
+
.Tooltip-size-thin {
|
|
166
|
+
width: ${TOOLTIP_WIDTH_THIN_REM}rem;
|
|
167
|
+
left: calc(50% - ${TOOLTIP_WIDTH_THIN_REM / 2}rem);
|
|
168
|
+
}
|
|
169
|
+
.Tooltip-size-normal {
|
|
170
|
+
width: ${TOOLTIP_WIDTH_NORMAL_REM}rem;
|
|
171
|
+
left: calc(50% - ${TOOLTIP_WIDTH_NORMAL_REM / 2}rem);
|
|
172
|
+
}
|
|
173
|
+
.Tooltip-size-wide {
|
|
174
|
+
width: ${TOOLTIP_WIDTH_WIDE_REM}rem;
|
|
175
|
+
left: calc(50% - ${TOOLTIP_WIDTH_WIDE_REM / 2}rem);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Arrow (beneath tooltip) */
|
|
179
|
+
.Tooltip-arrow,
|
|
180
|
+
.Tooltip-arrow-shadow {
|
|
181
|
+
/* Position beneath tooltip */
|
|
182
|
+
position: absolute;
|
|
183
|
+
top: -1.5rem;
|
|
184
|
+
font-size: 1.5rem;
|
|
185
|
+
left: calc(50% - 0.5rem);
|
|
186
|
+
width: 0;
|
|
187
|
+
display: inline-block;
|
|
188
|
+
|
|
189
|
+
/* Non-interactive */
|
|
190
|
+
pointer-events: none;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Arrow itself */
|
|
194
|
+
.Tooltip-arrow {
|
|
195
|
+
color: black;
|
|
196
|
+
z-index: 4;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Arrow shadow */
|
|
200
|
+
.Tooltip-arrow-shadow {
|
|
201
|
+
z-index: -1;
|
|
202
|
+
|
|
203
|
+
/* Blur a white version of the caret as a shadow */
|
|
204
|
+
color: white;
|
|
205
|
+
filter: blur(0.2rem);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* Classes to switch between arrow visible/hidden */
|
|
209
|
+
.Tooltip-arrow-visible-true {
|
|
210
|
+
opacity: 1;
|
|
211
|
+
}
|
|
212
|
+
.Tooltip-arrow-visible-false {
|
|
213
|
+
opacity: 0;
|
|
214
|
+
}
|
|
215
|
+
`;
|
|
216
|
+
|
|
217
|
+
/*------------------------------------------------------------------------*/
|
|
218
|
+
/* -------------------------------- State ------------------------------- */
|
|
219
|
+
/*------------------------------------------------------------------------*/
|
|
220
|
+
|
|
221
|
+
/* -------- State Definition -------- */
|
|
222
|
+
|
|
223
|
+
type State = {
|
|
224
|
+
// If true, the tooltip is visible
|
|
225
|
+
isVisible: boolean,
|
|
226
|
+
// Number of pixels to nudge the tooltip
|
|
227
|
+
nudgePx: number,
|
|
228
|
+
// If true, bottom left corner of tooltip should not be rounded
|
|
229
|
+
squareBottomLeft?: boolean,
|
|
230
|
+
// If true, bottom right corner of tooltip should not be rounded
|
|
231
|
+
squareBottomRight?: boolean,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/* ------------- Actions ------------ */
|
|
235
|
+
|
|
236
|
+
// Types of actions
|
|
237
|
+
enum ActionType {
|
|
238
|
+
// Make the tooltip visible
|
|
239
|
+
Show = 'Show',
|
|
240
|
+
// Make the tooltip invisible
|
|
241
|
+
Hide = 'Hide',
|
|
242
|
+
// Set nudge pixels
|
|
243
|
+
SetNudgePx = 'SetNudgePx',
|
|
244
|
+
// Set whether bottom left corner should be square
|
|
245
|
+
SetSquareBottomLeft = 'SetSquareBottomLeft',
|
|
246
|
+
// Set whether bottom right corner should be square
|
|
247
|
+
SetSquareBottomRight = 'SetSquareBottomRight',
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Action definitions
|
|
251
|
+
type Action = (
|
|
252
|
+
| {
|
|
253
|
+
// Action type
|
|
254
|
+
type: ActionType.SetNudgePx,
|
|
255
|
+
// Number of pixels to nudge the tooltip
|
|
256
|
+
nudgePx: number,
|
|
257
|
+
}
|
|
258
|
+
| {
|
|
259
|
+
// Action type
|
|
260
|
+
type: ActionType.SetSquareBottomLeft,
|
|
261
|
+
// If true, bottom left corner of tooltip should not be rounded
|
|
262
|
+
squareBottomLeft: boolean,
|
|
263
|
+
}
|
|
264
|
+
| {
|
|
265
|
+
// Action type
|
|
266
|
+
type: ActionType.SetSquareBottomRight,
|
|
267
|
+
// If true, bottom right corner of tooltip should not be rounded
|
|
268
|
+
squareBottomRight: boolean,
|
|
269
|
+
}
|
|
270
|
+
| {
|
|
271
|
+
// Action type
|
|
272
|
+
type: (
|
|
273
|
+
| ActionType.Show
|
|
274
|
+
| ActionType.Hide
|
|
275
|
+
),
|
|
276
|
+
}
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Reducer that executes actions
|
|
281
|
+
* @author Gabe Abrams
|
|
282
|
+
* @param state current state
|
|
283
|
+
* @param action action to execute
|
|
284
|
+
*/
|
|
285
|
+
const reducer = (state: State, action: Action): State => {
|
|
286
|
+
switch (action.type) {
|
|
287
|
+
case ActionType.Show: {
|
|
288
|
+
return {
|
|
289
|
+
...state,
|
|
290
|
+
isVisible: true,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
case ActionType.Hide: {
|
|
294
|
+
return {
|
|
295
|
+
...state,
|
|
296
|
+
isVisible: false,
|
|
297
|
+
nudgePx: 0,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
case ActionType.SetNudgePx: {
|
|
301
|
+
return {
|
|
302
|
+
...state,
|
|
303
|
+
nudgePx: action.nudgePx,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
case ActionType.SetSquareBottomLeft: {
|
|
307
|
+
return {
|
|
308
|
+
...state,
|
|
309
|
+
squareBottomLeft: action.squareBottomLeft,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
case ActionType.SetSquareBottomRight: {
|
|
313
|
+
return {
|
|
314
|
+
...state,
|
|
315
|
+
squareBottomRight: action.squareBottomRight,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
default: {
|
|
319
|
+
return state;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
/*------------------------------------------------------------------------*/
|
|
325
|
+
/* ------------------------------ Component ----------------------------- */
|
|
326
|
+
/*------------------------------------------------------------------------*/
|
|
327
|
+
|
|
328
|
+
const Tooltip: React.FC<Props> = (props) => {
|
|
329
|
+
/*------------------------------------------------------------------------*/
|
|
330
|
+
/* -------------------------------- Setup ------------------------------- */
|
|
331
|
+
/*------------------------------------------------------------------------*/
|
|
332
|
+
|
|
333
|
+
/* -------------- Props ------------- */
|
|
334
|
+
|
|
335
|
+
// Destructure all props
|
|
336
|
+
const {
|
|
337
|
+
icon,
|
|
338
|
+
text,
|
|
339
|
+
children,
|
|
340
|
+
wide,
|
|
341
|
+
thin,
|
|
342
|
+
} = props;
|
|
343
|
+
|
|
344
|
+
/* -------------- State ------------- */
|
|
345
|
+
|
|
346
|
+
// Initial state
|
|
347
|
+
const initialState: State = {
|
|
348
|
+
isVisible: false,
|
|
349
|
+
nudgePx: 0,
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// Initialize state
|
|
353
|
+
const [state, dispatch] = useReducer(reducer, initialState);
|
|
354
|
+
|
|
355
|
+
// Destructure common state
|
|
356
|
+
const {
|
|
357
|
+
isVisible,
|
|
358
|
+
nudgePx,
|
|
359
|
+
squareBottomLeft,
|
|
360
|
+
squareBottomRight,
|
|
361
|
+
} = state;
|
|
362
|
+
|
|
363
|
+
/* -------------- Refs -------------- */
|
|
364
|
+
|
|
365
|
+
// Initialize refs
|
|
366
|
+
const tooltipContainer = useRef<HTMLDivElement | null>(null);
|
|
367
|
+
const arrowElement = useRef<HTMLDivElement | null>(null);
|
|
368
|
+
|
|
369
|
+
/*------------------------------------------------------------------------*/
|
|
370
|
+
/* ------------------------- Lifecycle Functions ------------------------ */
|
|
371
|
+
/*------------------------------------------------------------------------*/
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Update (also called on mount)
|
|
375
|
+
* @author Gabe Abrams
|
|
376
|
+
*/
|
|
377
|
+
useEffect(
|
|
378
|
+
() => {
|
|
379
|
+
// Update tooltip container position
|
|
380
|
+
if (tooltipContainer.current && isVisible) {
|
|
381
|
+
// Get the tooltip container location
|
|
382
|
+
const tooltipContainerRect = (
|
|
383
|
+
tooltipContainer
|
|
384
|
+
.current
|
|
385
|
+
.getBoundingClientRect()
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
// Get the window width
|
|
389
|
+
const windowWidth = window.innerWidth;
|
|
390
|
+
|
|
391
|
+
// Calculate new nudge value
|
|
392
|
+
let newNudgePx: number;
|
|
393
|
+
if (tooltipContainerRect.left < 0) {
|
|
394
|
+
// Tooltip is off the left side of the screen
|
|
395
|
+
newNudgePx = -1 * tooltipContainerRect.left;
|
|
396
|
+
} else if (tooltipContainerRect.right > windowWidth) {
|
|
397
|
+
// Tooltip is off the right side of the screen
|
|
398
|
+
newNudgePx = -1 * (tooltipContainerRect.right - windowWidth);
|
|
399
|
+
} else {
|
|
400
|
+
// Tooltip is in the middle of the screen
|
|
401
|
+
newNudgePx = 0;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Update state
|
|
405
|
+
dispatch({
|
|
406
|
+
type: ActionType.SetNudgePx,
|
|
407
|
+
nudgePx: newNudgePx,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Update border rounding
|
|
412
|
+
if (arrowElement.current && isVisible) {
|
|
413
|
+
// Get the arrow location
|
|
414
|
+
const arrowRect = (
|
|
415
|
+
arrowElement
|
|
416
|
+
.current
|
|
417
|
+
.getBoundingClientRect()
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
// REM in pixels given font size
|
|
421
|
+
const remInPixels = Number.parseInt(
|
|
422
|
+
getComputedStyle(document.documentElement).fontSize,
|
|
423
|
+
10,
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
// Set bottom left corner rounding
|
|
427
|
+
dispatch({
|
|
428
|
+
type: ActionType.SetSquareBottomLeft,
|
|
429
|
+
squareBottomLeft: (
|
|
430
|
+
arrowRect.left < (TOOLTIP_BORDER_RADIUS_REM * remInPixels)
|
|
431
|
+
),
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Set bottom right corner rounding
|
|
435
|
+
dispatch({
|
|
436
|
+
type: ActionType.SetSquareBottomRight,
|
|
437
|
+
squareBottomRight: (
|
|
438
|
+
arrowRect.right
|
|
439
|
+
> window.innerWidth - (TOOLTIP_BORDER_RADIUS_REM * remInPixels)
|
|
440
|
+
),
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
[isVisible],
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
/*------------------------------------------------------------------------*/
|
|
448
|
+
/* ------------------------------- Render ------------------------------- */
|
|
449
|
+
/*------------------------------------------------------------------------*/
|
|
450
|
+
|
|
451
|
+
/*----------------------------------------*/
|
|
452
|
+
/* --------------- Main UI -------------- */
|
|
453
|
+
/*----------------------------------------*/
|
|
454
|
+
|
|
455
|
+
// Determine size
|
|
456
|
+
let size = 'normal';
|
|
457
|
+
if (wide) {
|
|
458
|
+
size = 'wide';
|
|
459
|
+
} else if (thin) {
|
|
460
|
+
size = 'thin';
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Render
|
|
464
|
+
return (
|
|
465
|
+
<div
|
|
466
|
+
className="Tooltip-outer-container"
|
|
467
|
+
aria-label={text}
|
|
468
|
+
onMouseEnter={() => {
|
|
469
|
+
dispatch({
|
|
470
|
+
type: ActionType.Show,
|
|
471
|
+
});
|
|
472
|
+
}}
|
|
473
|
+
onMouseLeave={() => {
|
|
474
|
+
dispatch({
|
|
475
|
+
type: ActionType.Hide,
|
|
476
|
+
});
|
|
477
|
+
}}
|
|
478
|
+
onFocus={() => {
|
|
479
|
+
dispatch({
|
|
480
|
+
type: ActionType.Show,
|
|
481
|
+
});
|
|
482
|
+
}}
|
|
483
|
+
onBlur={() => {
|
|
484
|
+
dispatch({
|
|
485
|
+
type: ActionType.Hide,
|
|
486
|
+
});
|
|
487
|
+
}}
|
|
488
|
+
>
|
|
489
|
+
{/* Style */}
|
|
490
|
+
<style>
|
|
491
|
+
{style}
|
|
492
|
+
</style>
|
|
493
|
+
|
|
494
|
+
{/* Children */}
|
|
495
|
+
{children}
|
|
496
|
+
|
|
497
|
+
{/* Arrow */}
|
|
498
|
+
<div
|
|
499
|
+
className={`Tooltip-arrow Tooltip-arrow-visible-${isVisible ? 'true' : 'false'}`}
|
|
500
|
+
ref={arrowElement}
|
|
501
|
+
>
|
|
502
|
+
<FontAwesomeIcon
|
|
503
|
+
icon={faCaretDown}
|
|
504
|
+
/>
|
|
505
|
+
</div>
|
|
506
|
+
<div className={`Tooltip-arrow-shadow Tooltip-arrow-visible-${isVisible ? 'true' : 'false'}`}>
|
|
507
|
+
<FontAwesomeIcon
|
|
508
|
+
icon={faCaretDown}
|
|
509
|
+
/>
|
|
510
|
+
</div>
|
|
511
|
+
|
|
512
|
+
{/* Tooltip */}
|
|
513
|
+
<div
|
|
514
|
+
className="Tooltip-tooltip-container"
|
|
515
|
+
>
|
|
516
|
+
<div
|
|
517
|
+
className={`Tooltip-tooltip Tooltip-tooltip-visible-${isVisible ? 'true' : 'false'} Tooltip-size-${size}`}
|
|
518
|
+
role="tooltip"
|
|
519
|
+
ref={tooltipContainer}
|
|
520
|
+
style={{
|
|
521
|
+
transform: `translateX(${nudgePx}px)`,
|
|
522
|
+
}}
|
|
523
|
+
>
|
|
524
|
+
{/* Box */}
|
|
525
|
+
<div className="Tooltip-box-highlight" />
|
|
526
|
+
<div
|
|
527
|
+
className="Tooltip-box"
|
|
528
|
+
style={{
|
|
529
|
+
borderBottomLeftRadius: squareBottomLeft ? 0 : undefined,
|
|
530
|
+
borderBottomRightRadius: squareBottomRight ? 0 : undefined,
|
|
531
|
+
}}
|
|
532
|
+
/>
|
|
533
|
+
<div
|
|
534
|
+
className="Tooltip-box-shadow"
|
|
535
|
+
style={{
|
|
536
|
+
borderBottomLeftRadius: squareBottomLeft ? 0 : undefined,
|
|
537
|
+
borderBottomRightRadius: squareBottomRight ? 0 : undefined,
|
|
538
|
+
}}
|
|
539
|
+
/>
|
|
540
|
+
|
|
541
|
+
{/* Contents */}
|
|
542
|
+
<div className="Tooltip-contents">
|
|
543
|
+
{/* Icon */}
|
|
544
|
+
{icon && (
|
|
545
|
+
<FontAwesomeIcon
|
|
546
|
+
icon={icon}
|
|
547
|
+
className="me-1"
|
|
548
|
+
/>
|
|
549
|
+
)}
|
|
550
|
+
|
|
551
|
+
{/* Text */}
|
|
552
|
+
<span className="Tooltip-text">
|
|
553
|
+
{text}
|
|
554
|
+
</span>
|
|
555
|
+
</div>
|
|
556
|
+
</div>
|
|
557
|
+
</div>
|
|
558
|
+
</div>
|
|
559
|
+
);
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
/*------------------------------------------------------------------------*/
|
|
563
|
+
/* ------------------------------- Wrap Up ------------------------------ */
|
|
564
|
+
/*------------------------------------------------------------------------*/
|
|
565
|
+
|
|
566
|
+
// Export component
|
|
567
|
+
export default Tooltip;
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// Import components
|
|
2
|
-
import AppWrapper, {
|
|
2
|
+
import AppWrapper, {
|
|
3
|
+
alert,
|
|
4
|
+
confirm,
|
|
5
|
+
showFatalError,
|
|
6
|
+
addFatalErrorHandler,
|
|
7
|
+
} from './components/AppWrapper';
|
|
3
8
|
import LoadingSpinner from './components/LoadingSpinner';
|
|
4
9
|
import ErrorBox from './components/ErrorBox';
|
|
5
10
|
import Modal from './components/Modal';
|
|
@@ -18,6 +23,8 @@ import LogReviewer from './components/LogReviewer';
|
|
|
18
23
|
import IntelliTable from './components/IntelliTable';
|
|
19
24
|
import CSVDownloadButton from './components/CSVDownloadButton';
|
|
20
25
|
import DBEntryManagerPanel from './components/DBEntryManagerPanel';
|
|
26
|
+
import Tooltip from './components/Tooltip';
|
|
27
|
+
import ToggleSwitch from './components/ToggleSwitch';
|
|
21
28
|
|
|
22
29
|
// Import errors
|
|
23
30
|
import ErrorWithCode from './errors/ErrorWithCode';
|
|
@@ -115,6 +122,8 @@ export {
|
|
|
115
122
|
IntelliTable,
|
|
116
123
|
CSVDownloadButton,
|
|
117
124
|
DBEntryManagerPanel,
|
|
125
|
+
Tooltip,
|
|
126
|
+
ToggleSwitch,
|
|
118
127
|
// Global functions
|
|
119
128
|
alert,
|
|
120
129
|
confirm,
|
|
@@ -162,6 +171,7 @@ export {
|
|
|
162
171
|
initClient,
|
|
163
172
|
visitServerEndpoint,
|
|
164
173
|
logClientEvent,
|
|
174
|
+
addFatalErrorHandler,
|
|
165
175
|
// Server helpers
|
|
166
176
|
initServer,
|
|
167
177
|
genRouteHandler,
|