@basic-ui/core 0.0.32 → 0.0.35

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/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "@basic-ui/core",
3
- "version": "0.0.32",
3
+ "version": "0.0.35",
4
4
  "description": "Accessible React Components used as building blocks for UI patterns",
5
5
  "author": "Lucas Terra <lucasterra7@gmail.com>",
6
6
  "license": "MIT",
7
- "private": false,
8
7
  "main": "./build/cjs/index.js",
9
8
  "module": "./build/esm/index.js",
10
9
  "jsnext:main": "./build/esm/index.js",
@@ -15,12 +14,12 @@
15
14
  ],
16
15
  "sideEffects": false,
17
16
  "scripts": {
18
- "build": "concurrently \"yarn:build:*\"",
17
+ "build": "run -T concurrently \"yarn:build:*\"",
19
18
  "build:dts": "tsc -p ./tsconfig.json --isolatedModules false --declaration --emitDeclarationOnly",
20
19
  "build:cjs": "rollup -c ../../rollup.config.js",
21
20
  "build:esm": "cross-env NODE_ENV=production BABEL_ENV=esm babel --config-file ../../babel.config.js ./src --extensions \".ts,.tsx,.js,.jsx\" --source-maps --out-dir ./build/esm --ignore \"**/*.story.tsx,**/*.story.ts,**/*.test.tsx,**/*.test.ts\"",
22
21
  "build-storybook": "build-storybook -c ../../scripts/storybook -o .out",
23
- "storybook": "start-storybook -p 9001 -c ../../scripts/storybook",
22
+ "storybook": "yarn run -T start-storybook -p 9001 -c ../../scripts/storybook",
24
23
  "start": "yarn run storybook",
25
24
  "serve": "http-server .out",
26
25
  "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}' --fix",
@@ -37,5 +36,5 @@
37
36
  "react": ">=16.14.0 || >=17.0.0",
38
37
  "react-dom": ">=16.14.0 || >=17.0.0"
39
38
  },
40
- "gitHead": "5d730ab4bfea19ceb7fb52d09139c81cb4c3863f"
39
+ "gitHead": "cbc74c92645db81f0f66c4bb52c69bf3f769bdcd"
41
40
  }
@@ -3,7 +3,7 @@ import { Tooltip } from './';
3
3
  import { storiesOf } from '@storybook/react';
4
4
  import { Popper } from '../Popper/Popper';
5
5
  import { InjectedTooltipProps } from './useTooltip';
6
- // import './styles.css';
6
+ import './styles.css';
7
7
 
8
8
  const StyledTooltip = forwardRef<HTMLDivElement, InjectedTooltipProps>(
9
9
  ({ children, anchorEl, ...props }, ref) => {
@@ -38,6 +38,22 @@ const Example = () => {
38
38
  );
39
39
  };
40
40
 
41
+ const ExampleWithHidingButton = () => {
42
+ const buttonRef = useRef();
43
+ return (
44
+ <ul style={{ margin: 100 }}>
45
+ {Array.from({ length: 20 }).map((_, index) => (
46
+ <li data-hide-child-buttons="" key={index}>
47
+ <Tooltip label={'Im groot'} as={StyledTooltip}>
48
+ <button ref={buttonRef}>Hello</button>
49
+ </Tooltip>
50
+ </li>
51
+ ))}
52
+ </ul>
53
+ );
54
+ };
55
+
41
56
  const stories = storiesOf('Components/Tooltip', module);
42
57
 
43
58
  stories.add('controlled', () => <Example />);
59
+ stories.add('with hiding button', () => <ExampleWithHidingButton />);
@@ -10,7 +10,7 @@ let restTimeout: number;
10
10
  function startRestTimer() {
11
11
  window.clearTimeout(restTimeout);
12
12
  restTimeout = window.setTimeout(() => {
13
- send(TooltipEventTypes.Rest, undefined);
13
+ send(Rest, undefined);
14
14
  }, 200);
15
15
  }
16
16
 
@@ -24,7 +24,7 @@ let leavingVisibleTimer: number;
24
24
  function startLeavingVisibleTimer() {
25
25
  window.clearTimeout(leavingVisibleTimer);
26
26
  leavingVisibleTimer = window.setTimeout(
27
- () => send(TooltipEventTypes.TimeComplete, undefined),
27
+ () => send(TimeComplete, undefined),
28
28
  100
29
29
  );
30
30
  }
@@ -36,41 +36,52 @@ function clearLeavingVisibleTimer() {
36
36
  ////////////////////////////////////////////////////////////////////////////////
37
37
  // State machine
38
38
 
39
- export enum TooltipStates {
40
- // Nothing goin' on
41
- Idle = 'IDLE',
42
-
43
- // We're considering showing the tooltip, but we're gonna wait a sec
44
- Focused = 'FOCUSED',
45
-
46
- // It's on!
47
- Visible = 'VISIBLE',
48
-
49
- // Focus has left, but we want to keep it visible for a sec
50
- LeavingVisible = 'LEAVING_VISIBLE',
51
-
52
- // The user clicked the tool, so we want to hide the thing, we can't just use
53
- // IDLE because we need to ignore mousemove, etc.
54
- Dismissed = 'DISMISSED',
55
- }
56
-
57
- export enum TooltipEventTypes {
58
- Blur = 'BLUR',
59
- Focus = 'FOCUS',
60
- GlobalMouseMove = 'GLOBAL_MOUSE_MOVE',
61
- MouseDown = 'MOUSE_DOWN',
62
- MouseEnter = 'MOUSE_ENTER',
63
- MouseLeave = 'MOUSE_LEAVE',
64
- MouseMove = 'MOUSE_MOVE',
65
- Rest = 'REST',
66
- SelectWithKeyboard = 'SELECT_WITH_KEYBOARD',
67
- TimeComplete = 'TIME_COMPLETE',
68
- }
39
+ export type TooltipStates =
40
+ | 'IDLE'
41
+ | 'FOCUSED'
42
+ | 'VISIBLE'
43
+ | 'LEAVING_VISIBLE'
44
+ | 'DISMISSED';
45
+
46
+ // Nothing goin' on
47
+ export const Idle = 'IDLE' as const;
48
+ // We're considering showing the tooltip, but we're gonna wait a sec
49
+ export const Focused = 'FOCUSED' as const;
50
+ // It's on!
51
+ export const Visible = 'VISIBLE' as const;
52
+ // Focus has left, but we want to keep it visible for a sec
53
+ export const LeavingVisible = 'LEAVING_VISIBLE' as const;
54
+ // The user clicked the tool, so we want to hide the thing, we can't just use
55
+ // IDLE because we need to ignore mousemove, etc.
56
+ export const Dismissed = 'DISMISSED' as const;
57
+
58
+ export type TooltipEventTypes =
59
+ | 'BLUR'
60
+ | 'FOCUS'
61
+ | 'GLOBAL_MOUSE_MOVE'
62
+ | 'MOUSE_DOWN'
63
+ | 'MOUSE_ENTER'
64
+ | 'MOUSE_LEAVE'
65
+ | 'MOUSE_MOVE'
66
+ | 'REST'
67
+ | 'SELECT_WITH_KEYBOARD'
68
+ | 'TIME_COMPLETE';
69
+
70
+ export const Blur = 'BLUR' as const;
71
+ export const Focus = 'FOCUS' as const;
72
+ export const GlobalMouseMove = 'GLOBAL_MOUSE_MOVE' as const;
73
+ export const MouseDown = 'MOUSE_DOWN' as const;
74
+ export const MouseEnter = 'MOUSE_ENTER' as const;
75
+ export const MouseLeave = 'MOUSE_LEAVE' as const;
76
+ export const MouseMove = 'MOUSE_MOVE' as const;
77
+ export const Rest = 'REST' as const;
78
+ export const SelectWithKeyboard = 'SELECT_WITH_KEYBOARD' as const;
79
+ export const TimeComplete = 'TIME_COMPLETE' as const;
69
80
 
70
81
  export const subscription = createSubscription();
71
82
  export const state = {
72
83
  current: {
73
- state: TooltipStates.Idle,
84
+ state: Idle as TooltipStates,
74
85
  id: '',
75
86
  },
76
87
  };
@@ -80,40 +91,40 @@ function clearContextId() {
80
91
  }
81
92
 
82
93
  const chart: GenericStateChart<TooltipStates, TooltipEventTypes> = {
83
- initial: TooltipStates.Idle,
94
+ initial: Idle,
84
95
  states: {
85
- [TooltipStates.Idle]: {
96
+ [Idle]: {
86
97
  enter: () => {
87
98
  clearContextId();
88
99
  },
89
100
  on: {
90
- [TooltipEventTypes.MouseEnter]: TooltipStates.Focused,
91
- [TooltipEventTypes.Focus]: TooltipStates.Visible,
101
+ [MouseEnter]: Focused,
102
+ [Focus]: Visible,
92
103
  },
93
104
  },
94
- [TooltipStates.Focused]: {
105
+ [Focused]: {
95
106
  enter: startRestTimer,
96
107
  leave: clearRestTimer,
97
108
  on: {
98
- [TooltipEventTypes.MouseMove]: TooltipStates.Focused,
99
- [TooltipEventTypes.MouseLeave]: TooltipStates.Idle,
100
- [TooltipEventTypes.MouseDown]: TooltipStates.Dismissed,
101
- [TooltipEventTypes.Blur]: TooltipStates.Idle,
102
- [TooltipEventTypes.Rest]: TooltipStates.Visible,
109
+ [MouseMove]: Focused,
110
+ [MouseLeave]: Idle,
111
+ [MouseDown]: Dismissed,
112
+ [Blur]: Idle,
113
+ [Rest]: Visible,
103
114
  },
104
115
  },
105
- [TooltipStates.Visible]: {
116
+ [Visible]: {
106
117
  on: {
107
- [TooltipEventTypes.Focus]: TooltipStates.Focused,
108
- [TooltipEventTypes.MouseEnter]: TooltipStates.Focused,
109
- [TooltipEventTypes.MouseLeave]: TooltipStates.LeavingVisible,
110
- [TooltipEventTypes.Blur]: TooltipStates.LeavingVisible,
111
- [TooltipEventTypes.MouseDown]: TooltipStates.Dismissed,
112
- [TooltipEventTypes.SelectWithKeyboard]: TooltipStates.Dismissed,
113
- [TooltipEventTypes.GlobalMouseMove]: TooltipStates.LeavingVisible,
118
+ [Focus]: Focused,
119
+ [MouseEnter]: Focused,
120
+ [MouseLeave]: LeavingVisible,
121
+ [Blur]: LeavingVisible,
122
+ [MouseDown]: Dismissed,
123
+ [SelectWithKeyboard]: Dismissed,
124
+ [GlobalMouseMove]: LeavingVisible,
114
125
  },
115
126
  },
116
- [TooltipStates.LeavingVisible]: {
127
+ [LeavingVisible]: {
117
128
  enter: () => {
118
129
  startLeavingVisibleTimer();
119
130
  },
@@ -122,18 +133,18 @@ const chart: GenericStateChart<TooltipStates, TooltipEventTypes> = {
122
133
  clearContextId();
123
134
  },
124
135
  on: {
125
- [TooltipEventTypes.MouseEnter]: TooltipStates.Visible,
126
- [TooltipEventTypes.Focus]: TooltipStates.Visible,
127
- [TooltipEventTypes.TimeComplete]: TooltipStates.Idle,
136
+ [MouseEnter]: Visible,
137
+ [Focus]: Visible,
138
+ [TimeComplete]: Idle,
128
139
  },
129
140
  },
130
- [TooltipStates.Dismissed]: {
141
+ [Dismissed]: {
131
142
  leave: () => {
132
143
  clearContextId();
133
144
  },
134
145
  on: {
135
- [TooltipEventTypes.MouseLeave]: TooltipStates.Idle,
136
- [TooltipEventTypes.Blur]: TooltipStates.Idle,
146
+ [MouseLeave]: Idle,
147
+ [Blur]: Idle,
137
148
  },
138
149
  },
139
150
  },
@@ -0,0 +1,17 @@
1
+ [data-hide-child-buttons=""] {
2
+ background-color: #ddd;
3
+ display: flex;
4
+ padding: 20px;
5
+ min-height: 62px;
6
+ box-sizing: border-box;
7
+ border-bottom: 1px solid #aaa;
8
+ justify-content: flex-end;
9
+ }
10
+
11
+ [data-hide-child-buttons=""] button {
12
+ display: none;
13
+ }
14
+
15
+ [data-hide-child-buttons=""]:hover button {
16
+ display: block;
17
+ }
@@ -6,8 +6,15 @@ import {
6
6
  send,
7
7
  state,
8
8
  subscription,
9
- TooltipEventTypes,
10
- TooltipStates,
9
+ Blur,
10
+ Focus,
11
+ LeavingVisible,
12
+ MouseDown,
13
+ MouseEnter,
14
+ MouseLeave,
15
+ MouseMove,
16
+ SelectWithKeyboard,
17
+ Visible,
11
18
  } from './stateMachine';
12
19
 
13
20
  export type ChildProps = React.HTMLAttributes<HTMLElement> &
@@ -41,46 +48,46 @@ export function useTooltip(
41
48
  useEffect(() => {
42
49
  subscription.subscribe(() => {
43
50
  setVisible(
44
- (state.current.state === TooltipStates.Visible ||
45
- state.current.state === TooltipStates.LeavingVisible) &&
51
+ (state.current.state === Visible ||
52
+ state.current.state === LeavingVisible) &&
46
53
  state.current.id === id
47
54
  );
48
55
  });
49
56
  }, [id]);
50
57
 
51
58
  function handleMouseEnter() {
52
- send(TooltipEventTypes.MouseEnter, { id });
59
+ send(MouseEnter, { id });
53
60
  }
54
61
 
55
62
  function handleMouseMove() {
56
- send(TooltipEventTypes.MouseMove, { id });
63
+ send(MouseMove, { id });
57
64
  }
58
65
 
59
66
  function handleMouseLeave() {
60
- send(TooltipEventTypes.MouseLeave);
67
+ send(MouseLeave);
61
68
  }
62
69
 
63
70
  function handleMouseDown() {
64
71
  // Allow quick click from one tool to another
65
72
  if (state.current.id === id) {
66
- send(TooltipEventTypes.MouseDown);
73
+ send(MouseDown);
67
74
  }
68
75
  }
69
76
 
70
77
  function handleFocus() {
71
- send(TooltipEventTypes.Focus, { id });
78
+ send(Focus, { id });
72
79
  }
73
80
 
74
81
  function handleBlur() {
75
82
  // Allow quick click from one tool to another
76
83
  if (state.current.id === id) {
77
- send(TooltipEventTypes.Blur, undefined);
84
+ send(Blur, undefined);
78
85
  }
79
86
  }
80
87
 
81
88
  function handleKeyDown(event: React.KeyboardEvent<HTMLElement>) {
82
89
  if (event.key === 'Enter' || event.key === ' ') {
83
- send(TooltipEventTypes.SelectWithKeyboard);
90
+ send(SelectWithKeyboard);
84
91
  }
85
92
  }
86
93
 
@@ -15,7 +15,9 @@ export interface GestureHandlersState {
15
15
  yInitial: number;
16
16
  yPrev: number;
17
17
  yVelocity: number;
18
+ startTime: number;
18
19
  down: boolean;
20
+ scrollLocked: boolean;
19
21
  }
20
22
 
21
23
  type SetStateFunc<S> = (
@@ -30,6 +32,7 @@ export interface GestureHandlersReturn {
30
32
 
31
33
  export interface GestureHandlerOptions {
32
34
  ensureTargetIsContainer?: boolean;
35
+ minTouchDelta?: number;
33
36
  }
34
37
 
35
38
  export const initialGestureHandlersState: GestureHandlersState = {
@@ -46,21 +49,33 @@ export const initialGestureHandlersState: GestureHandlersState = {
46
49
  yInitial: 0,
47
50
  yPrev: 0,
48
51
  yVelocity: 0,
52
+ startTime: 0,
49
53
  down: false,
54
+ scrollLocked: false,
50
55
  };
51
56
 
57
+ const FRAMERATE_CONST = 1000 / 60; // 60 fps
58
+ const VELOCITY_DEPR_FACTOR = FRAMERATE_CONST * 2;
59
+
52
60
  export function gestureHandlers(
53
61
  set: SetStateFunc<GestureHandlersState>,
54
62
  containerRef?: React.MutableRefObject<HTMLElement | null>,
55
63
  options: GestureHandlerOptions = {}
56
64
  ): GestureHandlersReturn {
57
- const { ensureTargetIsContainer = false } = options;
65
+ const { ensureTargetIsContainer = false, minTouchDelta = 0 } = options;
58
66
 
59
67
  // Common handlers
60
68
  const handleUp = () => {
61
69
  set((state: GestureHandlersState) => {
70
+ const deltaTime = Date.now() - state.startTime;
71
+ const xDelta = state.x - state.xInitial;
72
+ const yDelta = state.y - state.yInitial;
73
+ const xVelocity = calcVelocity(xDelta, deltaTime, state.xVelocity);
74
+ const yVelocity = calcVelocity(yDelta, deltaTime, state.yVelocity);
62
75
  const newState: GestureHandlersState = {
63
76
  ...state,
77
+ xVelocity,
78
+ yVelocity,
64
79
  target: null,
65
80
  down: false,
66
81
  };
@@ -88,28 +103,65 @@ export function gestureHandlers(
88
103
  yVelocity: 0,
89
104
  yInitial: pageY,
90
105
  yPrev: pageY,
106
+ startTime: Date.now(),
91
107
  down: true,
108
+ scrollLocked: false,
92
109
  };
93
110
 
94
111
  return newState;
95
112
  });
96
113
  };
97
114
 
115
+ function calcVelocity(
116
+ deltaSpace: number,
117
+ deltaTime: number,
118
+ prevVelocity: number
119
+ ) {
120
+ if (deltaTime < 1) {
121
+ deltaTime = 1;
122
+ }
123
+ const speed = deltaSpace / deltaTime;
124
+ const depr = 0.5 + Math.min(deltaTime / VELOCITY_DEPR_FACTOR, 0.5);
125
+ return speed * depr + prevVelocity * (1 - depr);
126
+ }
127
+
98
128
  function handleMove(e: MouseEvent) {
99
- const { pageX, movementX, pageY, movementY } = e;
100
- e.preventDefault && e.preventDefault(); // prevent drag & drop behaviour from browser
129
+ const { pageX, pageY } = e;
130
+ if (e.cancelable) {
131
+ // prevent drag & drop behaviour from browser
132
+ e.preventDefault && e.preventDefault();
133
+ }
101
134
 
102
135
  set((state: GestureHandlersState) => {
136
+ function getDeltaSum(
137
+ currentPos: number,
138
+ initialPos: number,
139
+ prevPos: number
140
+ ): number {
141
+ if (
142
+ state.scrollLocked ||
143
+ Math.abs(currentPos - initialPos) >= minTouchDelta
144
+ ) {
145
+ state.scrollLocked = true;
146
+ return currentPos - prevPos;
147
+ }
148
+ return 0;
149
+ }
150
+
103
151
  const target =
104
152
  (containerRef && containerRef.current) || (e as any).target;
105
153
 
154
+ const deltaTime = Date.now() - state.startTime;
155
+
106
156
  const width = target ? target.offsetWidth : NaN;
107
- const xDelta = pageX - state.xInitial;
157
+ const xDelta = state.xDelta + getDeltaSum(pageX, state.xInitial, state.x);
108
158
  const xDeltaPercent = (xDelta * 100) / width;
159
+ const xVelocity = calcVelocity(xDelta, deltaTime, state.xVelocity);
109
160
 
110
161
  const height = target ? target.offsetHeight : NaN;
111
- const yDelta = pageY - state.yInitial;
162
+ const yDelta = state.yDelta + getDeltaSum(pageY, state.yInitial, state.y);
112
163
  const yDeltaPercent = (yDelta * 100) / height;
164
+ const yVelocity = calcVelocity(yDelta, deltaTime, state.yVelocity);
113
165
 
114
166
  const newState = {
115
167
  ...state,
@@ -117,12 +169,12 @@ export function gestureHandlers(
117
169
  xDeltaPercent,
118
170
  x: pageX,
119
171
  xPrev: state.x,
120
- xVelocity: movementX,
172
+ xVelocity,
121
173
  yDelta,
122
174
  yDeltaPercent,
123
175
  y: pageY,
124
176
  yPrev: state.y,
125
- yVelocity: movementY,
177
+ yVelocity,
126
178
  };
127
179
 
128
180
  return newState;
@@ -133,7 +185,10 @@ export function gestureHandlers(
133
185
 
134
186
  /* eslint-disable @typescript-eslint/no-use-before-define */
135
187
  function handleTouchMove(e: TouchEvent) {
136
- e.preventDefault();
188
+ if (e.cancelable) {
189
+ // prevent drag & drop behaviour from browser
190
+ e.preventDefault();
191
+ }
137
192
  handleMove(e.touches.item(0) as any);
138
193
  }
139
194