react-align 1.1.6 → 2.0.2

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.
@@ -9,8 +9,8 @@ export type IconProps = {
9
9
  className?: string;
10
10
  name: string | Icons;
11
11
  size?: number;
12
+ style?: CSSProperties;
12
13
  onClick?: () => void;
13
- styles?: CSSProperties;
14
14
  };
15
15
 
16
16
  const IconStyles = (size?: number) =>
@@ -28,15 +28,15 @@ const Icon: React.FC<IconProps> = ({
28
28
  className,
29
29
  name,
30
30
  size,
31
+ style,
31
32
  onClick,
32
- styles,
33
33
  }) => {
34
34
  const LocalIconComponent = Icons[name as Icons];
35
35
  return (
36
36
  <LocalIconComponent
37
37
  className={className}
38
38
  {...IconStyles(size)}
39
- style={styles}
39
+ style={style}
40
40
  onClick={onClick}
41
41
  />
42
42
  );
package/src/context.tsx CHANGED
@@ -1,6 +1,10 @@
1
1
  import { createContext, useContext } from 'react';
2
+ import { Alignment } from '.';
2
3
 
3
- export const EditorModeContext = createContext<{
4
- enabled: boolean | undefined;
5
- }>({ enabled: undefined });
6
- export const useEditorMode = () => useContext(EditorModeContext);
4
+ export const Context = createContext<{
5
+ editing: boolean;
6
+ isDragging: boolean;
7
+ onAlignChange?: (location: string, align: Alignment) => void;
8
+ onExtend?: (location: string, extended: boolean) => void;
9
+ }>({ editing: false, isDragging: false });
10
+ export const useAlignContext = () => useContext(Context);
package/src/grid.css ADDED
@@ -0,0 +1,80 @@
1
+ /* Default component styles */
2
+ .wrapper {
3
+ height: 100%;
4
+ display: flex;
5
+ justify-content: space-between;
6
+ }
7
+
8
+ .section {
9
+ display: flex;
10
+ flex-direction: column;
11
+ justify-content: space-between;
12
+ }
13
+
14
+ .area {
15
+ display: flex;
16
+ border: 1px solid transparent;
17
+ box-sizing: border-box;
18
+ border-radius: 8px;
19
+ position: relative;
20
+ }
21
+
22
+ .area-transition-in {
23
+ transition: all 0.3s ease-in-out, min-height 0.5s ease-in-out 0.2s, min-width 0.5s ease-in-out 0.2s;
24
+ }
25
+
26
+ .area-transition-out {
27
+ transition: all 0.3s ease-in-out 0.4s, min-height 0.5s ease-in-out 0.2s, min-width 0.5s ease-in-out 0.2s;
28
+ }
29
+
30
+ .item {
31
+ position: relative;
32
+ border: 1px solid transparent;
33
+ box-sizing: border-box;
34
+ margin: 6px;
35
+ border-radius: 6px;
36
+ min-width: 70px;
37
+ min-height: 40px;
38
+ }
39
+
40
+ .stretch {
41
+ flex: auto;
42
+ }
43
+
44
+ .middle {
45
+ flex-grow: 0;
46
+ flex: auto;
47
+ }
48
+
49
+ .just-centered {
50
+ justify-content: center;
51
+ }
52
+
53
+ .just-end {
54
+ justify-content: flex-end;
55
+ }
56
+
57
+ .end {
58
+ align-items:flex-end;
59
+ }
60
+
61
+ .hide {
62
+ display: none;
63
+ }
64
+
65
+ .overlay {
66
+ position: absolute;
67
+ top: 0;
68
+ left: 0;
69
+ bottom: 0;
70
+ right: 0;
71
+ box-sizing: border-box;
72
+ background: rgba(0,0,0,0.6);
73
+ }
74
+
75
+ .overlay-buttons {
76
+ display: flex;
77
+ padding: 6px;
78
+ box-sizing: border-box;
79
+ justify-content: space-between;
80
+ }
package/src/index.tsx CHANGED
@@ -1,8 +1,8 @@
1
- export { default as GridWrapper } from "./Grid/GridWrapper";
2
- export { default as GridSection } from "./Grid/GridSection";
3
- export { default as GridArea } from "./Grid/GridArea";
4
- export { default as GridItem } from "./Grid/GridItem";
1
+ export { default as GridWrapper } from "./GridWrapper";
2
+ export { default as GridSection } from "./GridSection";
3
+ export { default as GridArea } from "./GridArea";
4
+ export { default as GridItem } from "./GridItem";
5
5
  export { default as Icon } from "./Icon";
6
6
 
7
7
  // eslint-disable-next-line prettier/prettier
8
- export type { Alignment } from "./Grid/GridArea";
8
+ export type { Alignment } from "./GridArea";
@@ -1,18 +0,0 @@
1
- import { CSSProperties, PropsWithChildren } from 'react';
2
- import '../grid.css';
3
- export declare type Alignment = 'start' | 'end' | 'centered';
4
- export declare type AreaProps<T = unknown> = {
5
- className?: string;
6
- vertical?: boolean;
7
- reverse?: boolean;
8
- stretch?: boolean;
9
- end?: boolean;
10
- droppable?: boolean;
11
- align?: Alignment;
12
- location: T;
13
- styles?: CSSProperties;
14
- editorStyles?: CSSProperties;
15
- iconColor?: string;
16
- onAlignChange?: (a: Alignment) => void;
17
- };
18
- export default function GridArea<T = unknown>({ className, vertical, reverse, stretch, end, droppable, align, onAlignChange, location, children, styles, editorStyles, iconColor, }: PropsWithChildren<AreaProps<T>>): JSX.Element;
@@ -1,25 +0,0 @@
1
- import { CSSProperties, PropsWithChildren } from 'react';
2
- import '../grid.css';
3
- export declare type ItemProps<T = unknown> = {
4
- className?: string;
5
- id: string;
6
- index: number;
7
- extendable?: boolean;
8
- extended?: boolean;
9
- draggable?: boolean;
10
- onReorder: (id: string, originalLocation: T, currentIndex: number, hoverIndex: number) => void;
11
- onMoveArea: (currentItem: string, dropLocation: T, originalLocation: T) => void;
12
- onExtend?: (id: string, extended: boolean) => void;
13
- location: T;
14
- end?: boolean;
15
- vertical?: boolean;
16
- styles?: CSSProperties;
17
- editorStyles?: CSSProperties;
18
- iconSize?: number;
19
- iconColor?: string;
20
- };
21
- export declare const ItemType: {
22
- ITEM: string;
23
- GROUP: string;
24
- };
25
- export default function GridItem<T = unknown>({ className, children, id, index, extendable, extended, draggable, onReorder, onMoveArea, onExtend, location, end, vertical, styles, editorStyles, iconSize, iconColor, }: PropsWithChildren<ItemProps<T>>): JSX.Element;
@@ -1,12 +0,0 @@
1
- import React, { CSSProperties } from 'react';
2
- import '../grid.css';
3
- export declare type GridWrapperProps = {
4
- className?: string;
5
- enabled?: boolean;
6
- vertical?: boolean;
7
- stretch?: boolean;
8
- styles?: CSSProperties;
9
- editorStyles?: CSSProperties;
10
- };
11
- declare const GridWrapper: React.FC<GridWrapperProps>;
12
- export default GridWrapper;
@@ -1,180 +0,0 @@
1
- import React, {
2
- CSSProperties,
3
- PropsWithChildren,
4
- useCallback,
5
- useMemo,
6
- } from 'react';
7
- import { useDrop, DropTargetMonitor } from 'react-dnd';
8
- import { ItemProps } from '../GridItem';
9
- import { useEditorMode } from '../../context';
10
- import Icon from '../../Icon';
11
- import '../grid.css';
12
-
13
- import { ItemType } from '../GridItem';
14
-
15
- export type Alignment = 'start' | 'end' | 'centered';
16
-
17
- export type AreaProps<T = unknown> = {
18
- className?: string;
19
- vertical?: boolean;
20
- reverse?: boolean;
21
- stretch?: boolean;
22
- end?: boolean;
23
- droppable?: boolean; // optional to override editorMode context (enabled param passed to GridWrapper) **Needs to be accompanied with GridItems draggable prop**
24
- align?: Alignment;
25
- location: T;
26
- // Extra customizable parts only for the really picky
27
- styles?: CSSProperties;
28
- editorStyles?: CSSProperties;
29
- iconColor?: string;
30
- onAlignChange?: (a: Alignment) => void;
31
- };
32
-
33
- export default function GridArea<T = unknown>({
34
- className,
35
- vertical,
36
- reverse,
37
- stretch,
38
- end,
39
- droppable,
40
- align,
41
- onAlignChange,
42
- location,
43
- children,
44
- // Picky stuff
45
- styles,
46
- editorStyles,
47
- iconColor = '#FFFFFF',
48
- }: PropsWithChildren<AreaProps<T>>) {
49
- const { enabled } = useEditorMode();
50
-
51
- const handleAlignChange = useCallback(
52
- (align: Alignment) => {
53
- switch (align) {
54
- case 'start':
55
- onAlignChange?.('centered');
56
- break;
57
- case 'centered':
58
- onAlignChange?.('end');
59
- break;
60
- default:
61
- onAlignChange?.('start');
62
- break;
63
- }
64
- },
65
- [onAlignChange]
66
- );
67
-
68
- // ***************************************
69
- // Drop logic
70
- const [{ isOver }, drop] = useDrop(() => ({
71
- accept: [ItemType.ITEM, ItemType.GROUP],
72
- drop: () => ({ location: location }),
73
- collect: (monitor: DropTargetMonitor) => ({
74
- isOver: monitor.isOver(),
75
- }),
76
- }));
77
- // ***************************************
78
-
79
- // ***************************************
80
- // Internal styles used
81
- const buttonStyle: CSSProperties = useMemo(
82
- () => ({
83
- position: 'absolute',
84
- left: vertical ? (end ? 0 : undefined) : '50%',
85
- right: vertical ? (!end ? 0 : undefined) : '50%',
86
- bottom: !vertical && !end ? 0 : vertical ? '50%' : undefined,
87
- top: vertical ? '50%' : end ? 0 : undefined,
88
- opacity: (droppable ?? enabled) && align ? 1 : 0,
89
- transition: 'all 0.5s ease-in-out',
90
- }),
91
- [vertical, end, droppable, enabled, align]
92
- );
93
-
94
- const mainStyles: CSSProperties = useMemo(
95
- () => ({
96
- opacity: isOver ? 0.8 : 1,
97
- minHeight: !React.Children.count(children) && !enabled ? '0px' : '26px',
98
- minWidth: !React.Children.count(children) && !enabled ? '0px' : '46px',
99
- }),
100
- [isOver, children, enabled]
101
- );
102
-
103
- const stylesFromProps: CSSProperties | undefined = enabled
104
- ? editorStyles
105
- : styles;
106
- // ***************************************
107
-
108
- // Rebuilds the GridItem children to receive their parent GridArea's 'end' and 'vertical' values.
109
- // Used to know where to align the overlay buttons (end) and how to extend the GridItems (vertical).
110
- const childrenWithParentProps = React.Children.map(children, child =>
111
- React.cloneElement(child as React.ReactElement<ItemProps<T>>, {
112
- end,
113
- vertical,
114
- location,
115
- })
116
- );
117
-
118
- return (
119
- <div
120
- ref={drop}
121
- className={`
122
- ${className}
123
- area
124
- ${stretch && 'stretch'}
125
- ${end && 'end'}
126
- ${
127
- align === 'centered'
128
- ? 'just-centered'
129
- : align === 'end'
130
- ? 'just-end'
131
- : 'start'
132
- }
133
- ${
134
- vertical
135
- ? reverse
136
- ? 'vertical-r'
137
- : 'vertical'
138
- : reverse
139
- ? 'horizontal-r'
140
- : 'horizontal'
141
- }
142
- ${enabled ? 'area-transition-in' : 'area-transition-out'}
143
- `}
144
- style={{ ...mainStyles, ...stylesFromProps }}
145
- >
146
- {childrenWithParentProps}
147
- <div style={buttonStyle}>
148
- <Icon
149
- name={
150
- align === 'centered'
151
- ? vertical
152
- ? 'alignCenterV'
153
- : 'alignCenter'
154
- : align === 'end'
155
- ? vertical
156
- ? 'alignEndV'
157
- : 'alignEnd'
158
- : vertical
159
- ? 'alignStartV'
160
- : 'alignStart'
161
- }
162
- styles={{
163
- color: iconColor,
164
- cursor:
165
- (droppable ?? enabled) &&
166
- align &&
167
- !!React.Children.count(children)
168
- ? 'pointer'
169
- : 'default',
170
- }}
171
- onClick={
172
- (droppable ?? enabled) && align && !!React.Children.count(children)
173
- ? () => handleAlignChange(align)
174
- : undefined
175
- }
176
- />
177
- </div>
178
- </div>
179
- );
180
- }
@@ -1,266 +0,0 @@
1
- import React, {
2
- useMemo,
3
- useRef,
4
- CSSProperties,
5
- useState,
6
- PropsWithChildren,
7
- } from 'react';
8
- import { useDrag, useDrop, DragSourceMonitor } from 'react-dnd';
9
- import { useEditorMode } from '../../context';
10
- import { DragItem } from '../interfaces';
11
-
12
- import Icon from '../../Icon';
13
-
14
- import '../grid.css';
15
-
16
- export type ItemProps<T = unknown> = {
17
- className?: string;
18
- id: string;
19
- index: number;
20
- extendable?: boolean;
21
- extended?: boolean;
22
- draggable?: boolean; // Optional to override or not use editorMode context. **Needs to be accompanied with GridAreas droppable prop**
23
- onReorder: (
24
- id: string,
25
- originalLocation: T,
26
- currentIndex: number,
27
- hoverIndex: number
28
- ) => void;
29
- onMoveArea: (
30
- currentItem: string,
31
- dropLocation: T,
32
- originalLocation: T
33
- ) => void;
34
- onExtend?: (id: string, extended: boolean) => void;
35
- // Props passed from parent.
36
- location: T;
37
- end?: boolean;
38
- vertical?: boolean;
39
- // Extra customizable parts only for the really picky.
40
- styles?: CSSProperties;
41
- editorStyles?: CSSProperties;
42
- iconSize?: number;
43
- iconColor?: string;
44
- };
45
-
46
- export const ItemType = {
47
- ITEM: 'react-align_item',
48
- GROUP: 'react-align_group',
49
- };
50
-
51
- export default function GridItem<T = unknown>({
52
- className,
53
- children,
54
- id,
55
- index,
56
- extendable,
57
- extended,
58
- draggable,
59
- onReorder,
60
- onMoveArea,
61
- onExtend,
62
- // Passed from parent (aka GridArea).
63
- location,
64
- end,
65
- vertical,
66
- // Picky stuff.
67
- styles,
68
- editorStyles,
69
- iconSize,
70
- iconColor = 'rgb(255, 255, 255)',
71
- }: PropsWithChildren<ItemProps<T>>) {
72
- const ref = useRef<HTMLDivElement>(null);
73
- const { enabled } = useEditorMode();
74
-
75
- const [isHovered, setHovered] = useState(false);
76
- const dragIndexRef = useRef<number | undefined>();
77
-
78
- const handleExtend = () => {
79
- if (!extendable || !onExtend) return;
80
- setHovered(false);
81
- onExtend(id, !extended);
82
- };
83
-
84
- // ***************************************
85
- // Drag n drop logic
86
- const [{ handlerId }, drop] = useDrop({
87
- accept: [ItemType.ITEM, ItemType.GROUP],
88
- collect(monitor) {
89
- return {
90
- handlerId: monitor.getHandlerId(),
91
- };
92
- },
93
- hover(item: DragItem, monitor) {
94
- if (!ref.current || !enabled || draggable) {
95
- return;
96
- }
97
-
98
- const dragIndex = item.index;
99
- const hoverIndex = index;
100
-
101
- if (dragIndex === hoverIndex) {
102
- return;
103
- }
104
-
105
- const hoverBoundingRect = ref.current?.getBoundingClientRect();
106
-
107
- const hoverMiddleY =
108
- (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
109
- const hoverMiddleX =
110
- (hoverBoundingRect.right - hoverBoundingRect.left) / 2;
111
-
112
- const clientOffset = monitor.getClientOffset();
113
- if (!clientOffset) return;
114
-
115
- const hoverClientY = clientOffset.y - hoverBoundingRect.top;
116
- const hoverClientX = clientOffset.x - hoverBoundingRect.left;
117
-
118
- if (vertical) {
119
- if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
120
- return;
121
- }
122
-
123
- if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
124
- return;
125
- }
126
- } else {
127
- if (dragIndex < hoverIndex && hoverClientX < hoverMiddleX) {
128
- return;
129
- }
130
-
131
- if (dragIndex > hoverIndex && hoverClientX > hoverMiddleX) {
132
- return;
133
- }
134
- }
135
-
136
- dragIndexRef.current = dragIndex;
137
- },
138
- drop(item) {
139
- if (dragIndexRef.current !== undefined) {
140
- onReorder(item.id, location, dragIndexRef.current, index);
141
- dragIndexRef.current = undefined;
142
- }
143
- },
144
- });
145
-
146
- const [{ isDragging }, drag, preview] = useDrag(
147
- {
148
- type: ItemType.ITEM,
149
- item: { id, index },
150
- canDrag: draggable ?? enabled,
151
- end: (item, monitor) => {
152
- const dropResults: {
153
- location: T;
154
- } | null = monitor.getDropResult();
155
-
156
- if (dropResults && dropResults.location !== location) {
157
- onMoveArea(item.id, dropResults.location, location);
158
- }
159
- },
160
- collect: (monitor: DragSourceMonitor) => ({
161
- isDragging: monitor.isDragging(),
162
- }),
163
- },
164
- [dragIndexRef]
165
- );
166
-
167
- preview(drop(ref));
168
- // ***************************************
169
-
170
- // ***************************************
171
- // External styles for editorMode or the vanilla grid
172
- const stylesFromProps: CSSProperties | undefined = enabled
173
- ? editorStyles
174
- : styles;
175
-
176
- const itemStyles: CSSProperties = useMemo(
177
- () => ({
178
- opacity: isDragging ? 0.5 : 1,
179
- minHeight: isHovered && enabled ? '40px' : undefined,
180
- width: !vertical && extended ? '100%' : undefined,
181
- minWidth:
182
- isHovered && enabled ? (extendable ? '70px' : '30px') : undefined,
183
- height: vertical && extended ? '100%' : undefined,
184
- }),
185
- [isDragging, isHovered, enabled, vertical, extended, extendable]
186
- );
187
-
188
- const containerStyle: CSSProperties = useMemo(
189
- () => ({
190
- position: 'relative',
191
- display: 'inline-block',
192
- minHeight: isHovered && enabled ? '40px' : undefined,
193
- width: !vertical && extended ? '100%' : undefined,
194
- minWidth:
195
- isHovered && enabled ? (extendable ? '70px' : '30px') : undefined,
196
- height: vertical && extended ? '100%' : undefined,
197
- }),
198
- [isHovered, enabled, vertical, extended, extendable]
199
- );
200
-
201
- const overlayStyles: CSSProperties = {
202
- position: 'absolute',
203
- top: '0',
204
- left: '0',
205
- width: '100%',
206
- height: '100%',
207
- boxSizing: 'border-box',
208
- background: 'rgba(0,0,0,0.6)',
209
- borderRadius: '6px',
210
- };
211
-
212
- const buttonStyles: CSSProperties = useMemo(
213
- () => ({
214
- display: 'flex',
215
- alignItems: end ? 'end' : 'start',
216
- justifyContent: 'space-between',
217
- padding: '6px',
218
- float: end ? 'right' : 'left',
219
- }),
220
- [end]
221
- );
222
- // ***************************************
223
-
224
- const childrenWithParentProps = React.Children.map(children, child =>
225
- React.cloneElement(child as React.ReactElement<{ extended: boolean }>, {
226
- extended: extended,
227
- })
228
- );
229
-
230
- return (
231
- <div
232
- id={id}
233
- ref={ref}
234
- data-handler-id={handlerId}
235
- className={`${className} item`}
236
- style={{ ...itemStyles, ...stylesFromProps }}
237
- onMouseEnter={() => setHovered(true)}
238
- onMouseLeave={() => setHovered(false)}
239
- >
240
- <div style={containerStyle}>
241
- {(draggable ?? enabled) && isHovered && (
242
- <div style={overlayStyles}>
243
- <div style={buttonStyles}>
244
- <div ref={drag}>
245
- <Icon
246
- name="moveArrows"
247
- size={iconSize}
248
- styles={{ color: iconColor }}
249
- />
250
- </div>
251
- {extendable && (
252
- <Icon
253
- name={vertical ? 'verticalExtend' : 'horizontalExtend'}
254
- size={iconSize}
255
- styles={{ color: iconColor, marginLeft: '8px' }}
256
- onClick={handleExtend}
257
- />
258
- )}
259
- </div>
260
- </div>
261
- )}
262
- {childrenWithParentProps}
263
- </div>
264
- </div>
265
- );
266
- }
@@ -1,46 +0,0 @@
1
- import React, { CSSProperties } from 'react';
2
- import { useEditorMode } from '../../context';
3
- import '../grid.css';
4
-
5
- export type GridSectionProps = {
6
- className?: string;
7
- horizontal?: boolean;
8
- stretch?: boolean;
9
- fixedWidth?: number;
10
- fixedHeight?: number;
11
- // Extra customizable parts only for the really picky
12
- styles?: CSSProperties;
13
- editorStyles?: CSSProperties;
14
- };
15
-
16
- const GridSection: React.FC<GridSectionProps> = ({
17
- className,
18
- horizontal,
19
- stretch,
20
- fixedWidth,
21
- fixedHeight,
22
- styles,
23
- editorStyles,
24
- children,
25
- }) => {
26
- const { enabled } = useEditorMode();
27
- const stylesFromProps: CSSProperties | undefined = enabled
28
- ? editorStyles
29
- : styles;
30
-
31
- return (
32
- <div
33
- className={`section ${className} ${horizontal &&
34
- 'horizontal'} ${stretch && 'stretch'}`}
35
- style={{
36
- ...stylesFromProps,
37
- height: fixedHeight + 'px',
38
- width: fixedWidth + 'px',
39
- }}
40
- >
41
- {children}
42
- </div>
43
- );
44
- };
45
-
46
- export default GridSection;