@khanacademy/wonder-blocks-layout 2.2.0 → 2.2.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.
@@ -1,298 +0,0 @@
1
- import * as React from "react";
2
- import type {StyleDeclaration} from "aphrodite";
3
- import type {StyleType} from "@khanacademy/wonder-blocks-core";
4
-
5
- import {InitialFallback} from "@khanacademy/wonder-blocks-core";
6
- import MediaLayoutContext from "./media-layout-context";
7
- import type {MediaSize, MediaSpec} from "../util/types";
8
- import type {Context} from "./media-layout-context";
9
- import {
10
- MEDIA_DEFAULT_SPEC,
11
- MEDIA_INTERNAL_SPEC,
12
- MEDIA_MODAL_SPEC,
13
- } from "../util/specs";
14
-
15
- const mediaQueryLists: {
16
- [key: string]: MediaQueryList;
17
- } = {};
18
-
19
- export type MockStyleSheet = Record<string, StyleType>;
20
-
21
- type Props = {
22
- /**
23
- * The contents to display. Alternatively, a function can be specified
24
- * that takes three arguments and should return some nodes to display.
25
- *
26
- * - mediaSize: The current size of the viewport (small/medium/large)
27
- * - mediaSpec: The current spec being used to manage the selection of
28
- * the mediaSize.
29
- * - styles: An Aphrodite stylesheet representing the current
30
- * stylesheet for this mediaSize (as specified in the
31
- * styleSheets prop).
32
- */
33
- children: (arg1: {
34
- mediaSize: MediaSize;
35
- mediaSpec: MediaSpec;
36
- styles: MockStyleSheet;
37
- }) => React.ReactNode;
38
- /**
39
- * Aphrodite stylesheets to pass through to the styles prop. The
40
- * stylesheets to render is based on the media size. "all" is always
41
- * rendered.
42
- */
43
- styleSheets?: {
44
- all?: StyleDeclaration;
45
- mdOrLarger?: StyleDeclaration;
46
- mdOrSmaller?: StyleDeclaration;
47
- small?: StyleDeclaration;
48
- medium?: StyleDeclaration;
49
- large?: StyleDeclaration;
50
- };
51
- };
52
-
53
- type State = {
54
- size?: MediaSize;
55
- };
56
-
57
- // If for some reason we're not able to resolve the current media size we
58
- // fall back to this state.
59
- const DEFAULT_SIZE = "large";
60
-
61
- type CombinedProps = Props & Context;
62
-
63
- /**
64
- * `MediaLayout` is responsible for changing the rendering of contents at
65
- * differently sized viewports. `MediaLayoutContext.Provider` can be used
66
- * to specify different breakpoint configurations. By default it uses
67
- * `MEDIA_DEFAULT_SPEC`. See media-layout-context.js for additiional options.
68
- */
69
- class MediaLayoutInternal extends React.Component<CombinedProps, State> {
70
- // A collection of thunks that's used to clean up event listeners
71
- // when the component is unmounted.
72
- cleanupThunks: Array<() => void>;
73
-
74
- constructor(props: CombinedProps) {
75
- super(props);
76
- this.state = {
77
- size: undefined,
78
- };
79
- this.cleanupThunks = [];
80
- }
81
-
82
- componentDidMount() {
83
- // TODO(WB-534): handle changes to mediaSpec prop
84
- const entries: Array<
85
- [
86
- MediaSize,
87
- {
88
- query: string;
89
- },
90
- ]
91
- > = Object.entries(this.props.mediaSpec) as any;
92
-
93
- for (const [size, spec] of entries) {
94
- const mql = mediaQueryLists[spec.query];
95
- // during SSR there are no MediaQueryLists
96
- if (!mql) {
97
- continue;
98
- }
99
- const listener = (e: MediaQueryListEvent) => {
100
- if (e.matches) {
101
- this.setState({size});
102
- }
103
- };
104
- mql.addListener(listener);
105
- this.cleanupThunks.push(() => mql.removeListener(listener));
106
- }
107
- }
108
-
109
- componentWillUnmount() {
110
- // Remove our listeners.
111
- this.cleanupThunks.forEach((cleaup) => cleaup());
112
- }
113
-
114
- getCurrentSize(spec: MediaSpec): MediaSize {
115
- // If we have a state with the current size in it then we always want
116
- // to use that. This will happen if the viewport changes sizes after
117
- // we've already initialized.
118
- if (this.state.size) {
119
- return this.state.size;
120
- } else {
121
- const entries: Array<
122
- [
123
- MediaSize,
124
- {
125
- query: string;
126
- },
127
- ]
128
- > = Object.entries(this.props.mediaSpec) as any;
129
-
130
- for (const [size, spec] of entries) {
131
- const mql = mediaQueryLists[spec.query];
132
- if (mql.matches) {
133
- return size;
134
- }
135
- }
136
- }
137
-
138
- return DEFAULT_SIZE;
139
- }
140
-
141
- // We assume that we're running on an unsupported environment) if there is
142
- // no window object or matchMedia function available.
143
- isUnsupportedEnvironment() {
144
- return typeof window === "undefined" || !window.matchMedia;
145
- }
146
-
147
- // Generate a mock Aphrodite StyleSheet based upon the current mediaSize
148
- // We do this by looking at all of the stylesheets specified in the
149
- // styleSheets prop and then all of the individual styles. We merge the
150
- // styles together
151
- // TODO(WB-533): move to util.js to make it easier to test
152
- getMockStyleSheet(mediaSize: MediaSize) {
153
- const {styleSheets} = this.props;
154
-
155
- const mockStyleSheet: MockStyleSheet = {};
156
-
157
- // If no stylesheets were specified then we just return an empty object
158
- if (!styleSheets) {
159
- return mockStyleSheet;
160
- }
161
-
162
- // Go through all of the stylesheets that were specified
163
- for (const styleSize of Object.keys(styleSheets)) {
164
- // @ts-expect-error [FEI-5019] - TS7053 - Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ all?: StyleDeclaration | undefined; mdOrLarger?: StyleDeclaration | undefined; mdOrSmaller?: StyleDeclaration | undefined; }'.
165
- const styleSheet = styleSheets[styleSize];
166
-
167
- if (!styleSheet) {
168
- continue;
169
- }
170
-
171
- // And then through each key of each stylesheet
172
- for (const name of Object.keys(styleSheet)) {
173
- if (
174
- Object.prototype.hasOwnProperty.call(mockStyleSheet, name)
175
- ) {
176
- continue;
177
- }
178
-
179
- // We create an entry that combines the values from all of
180
- // the stylesheets together in least-specific to most-specific
181
- // priority (thus small/medium/large styles will always have
182
- // precedence over "all" or mdOrSmaller/mdOrLarger/etc.).
183
- mockStyleSheet[name] = [
184
- styleSheets.all && styleSheets.all[name],
185
- mediaSize === "small" && [
186
- styleSheets.mdOrSmaller &&
187
- styleSheets.mdOrSmaller[name],
188
- styleSheets.small && styleSheets.small[name],
189
- ],
190
- mediaSize === "medium" && [
191
- styleSheets.mdOrSmaller &&
192
- styleSheets.mdOrSmaller[name],
193
- styleSheets.mdOrLarger && styleSheets.mdOrLarger[name],
194
- styleSheets.medium && styleSheets.medium[name],
195
- ],
196
- mediaSize === "large" && [
197
- styleSheets.mdOrLarger && styleSheets.mdOrLarger[name],
198
- styleSheets.large && styleSheets.large[name],
199
- ],
200
- ];
201
- }
202
- }
203
-
204
- return mockStyleSheet;
205
- }
206
-
207
- renderContent(initialRender: boolean): React.ReactNode {
208
- const {children, mediaSpec, ssrSize, overrideSize} = this.props;
209
-
210
- const queries = [
211
- ...Object.values(MEDIA_DEFAULT_SPEC).map((spec: any) => spec.query),
212
- ...Object.values(MEDIA_INTERNAL_SPEC).map(
213
- (spec: any) => spec.query,
214
- ),
215
- ...Object.values(MEDIA_MODAL_SPEC).map((spec: any) => spec.query),
216
- ...Object.values(mediaSpec).map((spec: any) => spec.query),
217
- ];
218
-
219
- // We need to create the MediaQueryLists during the first render in order
220
- // to query whether any of them match.
221
- if (!initialRender) {
222
- for (const query of queries.filter(
223
- (query) => !mediaQueryLists[query],
224
- )) {
225
- mediaQueryLists[query] = window.matchMedia(query);
226
- }
227
- }
228
-
229
- // We need to figure out what the current media size is
230
- // If an override has been specified, we use that.
231
- // If we're rendering on the server then we use the default
232
- // SSR rendering size.
233
- // Otherwise we attempt to get the current size based on
234
- // the current MediaSpec.
235
- const mediaSize =
236
- overrideSize ||
237
- (initialRender && ssrSize) ||
238
- this.getCurrentSize(mediaSpec);
239
-
240
- // Generate a mock stylesheet
241
- const styles = this.getMockStyleSheet(mediaSize);
242
-
243
- return children({mediaSize, mediaSpec, styles});
244
- }
245
-
246
- render() {
247
- return (
248
- <InitialFallback fallback={() => this.renderContent(true)}>
249
- {() => this.renderContent(this.isUnsupportedEnvironment())}
250
- </InitialFallback>
251
- );
252
- }
253
- }
254
-
255
- /**
256
- * ***NOTE: The MediaLayout component is being deprecated. Do not use this!!***
257
- *
258
- * MediaLayout is a container component that accepts a `styleSheets` object,
259
- * whose keys are media sizes. It listens for changes to the current media
260
- * size and passes the current `mediaSize`, `mediaSpec`, and `styles` to
261
- * `children`, which is a render function taking those three values as an
262
- * object.
263
- *
264
- * Valid keys for the `styleSheets` object are (in order of precedence):
265
- * - `small`, `medium`, `large`
266
- * - `mdOrSmaller`, `mdOrLarger`
267
- * - `all`
268
- *
269
- * `MediaLayout` will merge style rules from multiple styles that match the
270
- * current media query, e.g. `"(min-width: 1024px)"`.
271
- *
272
- * The `mediaSpec` is an object with one or more of the following keys:
273
- * `small`, `medium`, or `large`. Each value contains the following data:
274
- * - `query: string` e.g. "(min-width: 1024px)"
275
- * - `totalColumns: number`
276
- * - `gutterWidth: number`
277
- * - `marginWidth: number`
278
- * - `maxWidth: number`
279
- */
280
- export default class MediaLayout extends React.Component<Props> {
281
- render(): React.ReactNode {
282
- // We listen to the MediaLayoutContext to see what defaults we're
283
- // being given (this can be overriden by wrapping this component in
284
- // a MediaLayoutContext.Consumer).
285
- return (
286
- <MediaLayoutContext.Consumer>
287
- {({overrideSize, ssrSize, mediaSpec}) => (
288
- <MediaLayoutInternal
289
- {...this.props}
290
- overrideSize={overrideSize}
291
- ssrSize={ssrSize}
292
- mediaSpec={mediaSpec}
293
- />
294
- )}
295
- </MediaLayoutContext.Consumer>
296
- );
297
- }
298
- }
@@ -1,27 +0,0 @@
1
- import * as React from "react";
2
- import {StyleSheet} from "aphrodite";
3
-
4
- import {View} from "@khanacademy/wonder-blocks-core";
5
- import type {StyleType} from "@khanacademy/wonder-blocks-core";
6
-
7
- type Props = {
8
- style?: StyleType;
9
- };
10
-
11
- /**
12
- * Expands to fill space between sibling components.
13
- *
14
- * Assumes parent is a View.
15
- */
16
- export default class Spring extends React.Component<Props> {
17
- render(): React.ReactNode {
18
- const {style} = this.props;
19
- return <View aria-hidden="true" style={[styles.grow, style]} />;
20
- }
21
- }
22
-
23
- const styles = StyleSheet.create({
24
- grow: {
25
- flexGrow: 1,
26
- },
27
- });
@@ -1,32 +0,0 @@
1
- import * as React from "react";
2
-
3
- import {View} from "@khanacademy/wonder-blocks-core";
4
- import type {StyleType} from "@khanacademy/wonder-blocks-core";
5
-
6
- type Props = {
7
- size: number;
8
- style?: StyleType;
9
- };
10
-
11
- /**
12
- * A component for inserting fixed space between components.
13
- *
14
- * Assumes parent is a View.
15
- */
16
- export default class Strut extends React.Component<Props> {
17
- render(): React.ReactNode {
18
- const {size, style} = this.props;
19
- return <View aria-hidden="true" style={[strutStyle(size), style]} />;
20
- }
21
- }
22
-
23
- const strutStyle = (size: number) => {
24
- return {
25
- width: size,
26
- MsFlexBasis: size,
27
- MsFlexPreferredSize: size,
28
- WebkitFlexBasis: size,
29
- flexBasis: size,
30
- flexShrink: 0,
31
- };
32
- };
package/src/index.ts DELETED
@@ -1,17 +0,0 @@
1
- import type {MediaQuery, MediaSize, MediaSpec} from "./util/types";
2
- import type {Context} from "./components/media-layout-context";
3
- import type {MockStyleSheet} from "./components/media-layout";
4
-
5
- export {default as MediaLayout} from "./components/media-layout";
6
- export {default as MediaLayoutContext} from "./components/media-layout-context";
7
- export {default as Spring} from "./components/spring";
8
- export {default as Strut} from "./components/strut";
9
- export * from "./util/specs";
10
- export {queryMatchesSize} from "./util/util";
11
- export type {
12
- MediaQuery,
13
- MediaSize,
14
- MediaSpec,
15
- MockStyleSheet,
16
- Context as MediaLayoutContextValue,
17
- };
package/src/util/specs.ts DELETED
@@ -1,58 +0,0 @@
1
- import {spacing} from "@khanacademy/wonder-blocks-tokens";
2
-
3
- import type {MediaSize, MediaSpec} from "./types";
4
-
5
- // All possible valid media sizes
6
- export const VALID_MEDIA_SIZES: Array<MediaSize> = ["small", "medium", "large"];
7
-
8
- const mediaDefaultSpecLargeMarginWidth = spacing.large_24;
9
-
10
- // The default spec for media layout, currently available in
11
- // three different settings (roughly mobile, tablet, and desktop).
12
- export const MEDIA_DEFAULT_SPEC: MediaSpec = {
13
- small: {
14
- query: "(max-width: 767px)",
15
- totalColumns: 4,
16
- gutterWidth: spacing.medium_16,
17
- marginWidth: spacing.medium_16,
18
- },
19
- medium: {
20
- query: "(min-width: 768px) and (max-width: 1023px)",
21
- totalColumns: 8,
22
- gutterWidth: spacing.xLarge_32,
23
- marginWidth: spacing.large_24,
24
- },
25
- large: {
26
- query: "(min-width: 1024px)",
27
- totalColumns: 12,
28
- gutterWidth: spacing.xLarge_32,
29
- marginWidth: mediaDefaultSpecLargeMarginWidth,
30
- maxWidth: 1120 + mediaDefaultSpecLargeMarginWidth * 2,
31
- },
32
- };
33
-
34
- // Used for internal tools
35
- export const MEDIA_INTERNAL_SPEC: MediaSpec = {
36
- large: {
37
- query: "(min-width: 1px)",
38
- totalColumns: 12,
39
- gutterWidth: spacing.xLarge_32,
40
- marginWidth: spacing.medium_16,
41
- },
42
- };
43
-
44
- // The default used for modals
45
- export const MEDIA_MODAL_SPEC: MediaSpec = {
46
- small: {
47
- query: "(max-width: 767px)",
48
- totalColumns: 4,
49
- gutterWidth: spacing.medium_16,
50
- marginWidth: spacing.medium_16,
51
- },
52
- large: {
53
- query: "(min-width: 768px)",
54
- totalColumns: 12,
55
- gutterWidth: spacing.xLarge_32,
56
- marginWidth: spacing.xxLarge_48,
57
- },
58
- };