Implement breakpointObserver.

Drop breakpoints constant export from Breakpoint component.
Expose breakpoints constant separately and freeze it.
Change `match` event to 'change`, because it is now based on store and captures initial value change.

Issues:
- Wrong exports in types/index.d.ts
- Typedefs lost from docs.
This commit is contained in:
Harald Brunner 2022-02-12 16:31:08 +01:00
commit 2cd2872274
10 changed files with 115 additions and 121 deletions

View file

@ -348,21 +348,12 @@ None.
## `Breakpoint` ## `Breakpoint`
### Types
```ts
export type BreakpointSize = "sm" | "md" | "lg" | "xlg" | "max";
export type BreakpointValue = 320 | 672 | 1056 | 1312 | 1584;
```
### Props ### Props
| Prop name | Kind | Reactive | Type | Default value | Description | | Prop name | Kind | Reactive | Type | Default value | Description |
| :---------- | :----------------- | :------- | :--------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------- | | :-------- | :--------------- | :------- | :------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------- |
| sizes | <code>let</code> | Yes | <code>Record<BreakpointSize, boolean></code> | <code>{ sm: false, md: false, lg: false, xlg: false, max: false, }</code> | Carbon grid sizes as an object | | sizes | <code>let</code> | Yes | <code>Record<BreakpointSize, boolean></code> | <code>{ sm: false, md: false, lg: false, xlg: false, max: false, }</code> | Carbon grid sizes as an object |
| size | <code>let</code> | Yes | <code>BreakpointSize</code> | <code>undefined</code> | Determine the current Carbon grid breakpoint size | | size | <code>let</code> | Yes | <code>BreakpointSize</code> | <code>undefined</code> | Determine the current Carbon grid breakpoint size |
| breakpoints | <code>const</code> | No | <code>Record<BreakpointSize, BreakpointValue></code> | <code>{ sm: 320, md: 672, lg: 1056, xlg: 1312, max: 1584, }</code> | Reference the Carbon grid breakpoints |
### Slots ### Slots
@ -374,7 +365,7 @@ export type BreakpointValue = 320 | 672 | 1056 | 1312 | 1584;
| Event name | Type | Detail | | Event name | Type | Detail |
| :--------- | :--------- | :----------------------------------------------------------------------- | | :--------- | :--------- | :----------------------------------------------------------------------- |
| match | dispatched | <code>{ size: BreakpointSize; breakpointValue: BreakpointValue; }</code> | | change | dispatched | <code>{ size: BreakpointSize; breakpointValue: BreakpointValue; }</code> |
## `Button` ## `Button`

View file

@ -387,17 +387,6 @@
"isFunctionDeclaration": false, "isFunctionDeclaration": false,
"constant": false, "constant": false,
"reactive": true "reactive": true
},
{
"name": "breakpoints",
"kind": "const",
"description": "Reference the Carbon grid breakpoints",
"type": "Record<BreakpointSize, BreakpointValue>",
"value": "{ sm: 320, md: 672, lg: 1056, xlg: 1312, max: 1584, }",
"isFunction": false,
"isFunctionDeclaration": false,
"constant": true,
"reactive": false
} }
], ],
"slots": [ "slots": [
@ -410,22 +399,11 @@
"events": [ "events": [
{ {
"type": "dispatched", "type": "dispatched",
"name": "match", "name": "change",
"detail": "{ size: BreakpointSize; breakpointValue: BreakpointValue; }" "detail": "{ size: BreakpointSize; breakpointValue: BreakpointValue; }"
} }
], ],
"typedefs": [ "typedefs": []
{
"type": "\"sm\" | \"md\" | \"lg\" | \"xlg\" | \"max\"",
"name": "BreakpointSize",
"ts": "type BreakpointSize = \"sm\" | \"md\" | \"lg\" | \"xlg\" | \"max\""
},
{
"type": "320 | 672 | 1056 | 1312 | 1584",
"name": "BreakpointValue",
"ts": "type BreakpointValue = 320 | 672 | 1056 | 1312 | 1584"
}
]
}, },
{ {
"moduleName": "Button", "moduleName": "Button",

View file

@ -1,8 +1,6 @@
<script> <script>
/** /**
* @typedef {"sm" | "md" | "lg" | "xlg" | "max"} BreakpointSize * @event {{ size: BreakpointSize; breakpointValue: BreakpointValue; }} change
* @typedef {320 | 672 | 1056 | 1312 | 1584} BreakpointValue
* @event {{ size: BreakpointSize; breakpointValue: BreakpointValue; }} match
* @slot {{ size: BreakpointSize; sizes: Record<BreakpointSize, boolean>; }} * @slot {{ size: BreakpointSize; sizes: Record<BreakpointSize, boolean>; }}
*/ */
@ -24,77 +22,23 @@
max: false, max: false,
}; };
/** import { createEventDispatcher } from "svelte";
* Reference the Carbon grid breakpoints import { breakpointObserver } from "./breakpointObserver";
* @type {Record<BreakpointSize, BreakpointValue>} import { breakpoints } from "./constants";
*/
export const breakpoints = {
sm: 320,
md: 672,
lg: 1056,
xlg: 1312,
max: 1584,
};
import { createEventDispatcher, onMount } from "svelte";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const observer = breakpointObserver();
onMount(() => { $: size = $observer;
const width = window.innerWidth; $: sizes = {
sm: size == "sm",
if (width > breakpoints.max) size = "max"; md: size == "md",
else if (width < breakpoints.md) size = "sm"; lg: size == "lg",
else if (width >= breakpoints.md && width <= breakpoints.lg) size = "md"; xlg: size == "xlg",
else if (width >= breakpoints.lg && width <= breakpoints.xlg) size = "lg"; max: size == "max",
else if (width >= breakpoints.xlg && width <= breakpoints.max) size = "xlg"; };
$: if (size != undefined)
const match = { dispatch("change", { size, breakpointValue: breakpoints[size] });
sm: window.matchMedia(`(max-width: ${breakpoints.md}px)`),
md: window.matchMedia(
`(min-width: ${breakpoints.md}px) and (max-width: ${breakpoints.lg}px)`
),
lg: window.matchMedia(
`(min-width: ${breakpoints.lg}px) and (max-width: ${breakpoints.xlg}px)`
),
xlg: window.matchMedia(
`(min-width: ${breakpoints.xlg}px) and (max-width: ${breakpoints.max}px)`
),
max: window.matchMedia(`(min-width: ${breakpoints.max}px)`),
};
const matchers = Object.entries(match);
const matchersBySize = Object.fromEntries(
matchers.map(([size, queryList]) => [queryList.media, size])
);
function handleChange({ matches, media }) {
const size = matchersBySize[media];
sizes = { ...sizes };
sizes[size] = matches;
if (matches)
dispatch("match", { size, breakpointValue: breakpoints[size] });
}
matchers.forEach(([size, queryList]) =>
queryList.addEventListener("change", handleChange)
);
return () => {
matchers.forEach(([size, queryList]) =>
queryList.removeEventListener("change", handleChange)
);
};
});
$: {
if (sizes.sm) size = "sm";
if (sizes.md) size = "md";
if (sizes.lg) size = "lg";
if (sizes.xlg) size = "xlg";
if (sizes.max) size = "max";
}
</script> </script>
<slot size="{size}" sizes="{sizes}" /> <slot size="{size}" sizes="{sizes}" />

View file

@ -0,0 +1,71 @@
/// <reference path="./types.d.ts"/>
import { onMount } from "svelte";
import { derived, writable } from "svelte/store";
import { breakpoints } from "./constants";
/**
* Creates a readable store that returns the current {@link BreakpointSize}.
* It also provides functions for creating derived stores used to do comparisons.
*/
export function breakpointObserver() {
const store = writable(undefined);
onMount(() => {
/** @type {Record<BreakpointSize, MediaQueryList>} */
const match = {
sm: window.matchMedia(`(max-width: ${breakpoints.md}px)`),
md: window.matchMedia(
`(min-width: ${breakpoints.md}px) and (max-width: ${breakpoints.lg}px)`
),
lg: window.matchMedia(
`(min-width: ${breakpoints.lg}px) and (max-width: ${breakpoints.xlg}px)`
),
xlg: window.matchMedia(
`(min-width: ${breakpoints.xlg}px) and (max-width: ${breakpoints.max}px)`
),
max: window.matchMedia(`(min-width: ${breakpoints.max}px)`),
};
const matchers = Object.entries(match);
const sizeByMedia = Object.fromEntries(
matchers.map(([size, queryList]) => [queryList.media, size])
);
const size = matchers.find(([size, queryList]) => queryList.matches)[0];
store.set(size);
/** @type {(e: MediaQueryListEvent) => void} */
function handleChange({ matches, media }) {
const size = sizeByMedia[media];
if (matches) store.set(size);
}
matchers.forEach(([size, queryList]) =>
queryList.addEventListener("change", handleChange)
);
return () => {
matchers.forEach(([size, queryList]) =>
queryList.removeEventListener("change", handleChange)
);
};
});
return {
subscribe: store.subscribe,
/**
* Returns a store readable store that returns whether the current
* breakpoint smaller than {@link size}.
* @param {BreakpointSize} size Size to compare against.
*/
smallerThan: (size) =>
derived(store, ($size) => breakpoints[$size] < breakpoints[size]),
/**
* Returns a store readable store that returns whether the current
* breakpoint larger than {@link size}.
* @param {BreakpointSize} size Size to compare against.
*/
largerThan: (size) =>
derived(store, ($size) => breakpoints[$size] > breakpoints[size]),
};
}

View file

@ -0,0 +1,13 @@
/// <reference path="./types.d.ts"/>
/**
* Pixel sizes of Carbon grid breakpoints.
* @type {Record<BreakpointSize, BreakpointValue>}
*/
export const breakpoints = Object.freeze({
sm: 320,
md: 672,
lg: 1056,
xlg: 1312,
max: 1584,
});

View file

@ -1 +1,3 @@
export { default as Breakpoint } from "./Breakpoint.svelte"; export { default as Breakpoint } from "./Breakpoint.svelte";
export { breakpointObserver } from "./breakpointObserver";
export { breakpoints } from "./constants";

2
src/Breakpoint/types.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
declare type BreakpointSize = "sm" | "md" | "lg" | "xlg" | "max";
declare type BreakpointValue = 320 | 672 | 1056 | 1312 | 1584;

View file

@ -1,7 +1,7 @@
export { Accordion, AccordionItem, AccordionSkeleton } from "./Accordion"; export { Accordion, AccordionItem, AccordionSkeleton } from "./Accordion";
export { AspectRatio } from "./AspectRatio"; export { AspectRatio } from "./AspectRatio";
export { Breadcrumb, BreadcrumbItem, BreadcrumbSkeleton } from "./Breadcrumb"; export { Breadcrumb, BreadcrumbItem, BreadcrumbSkeleton } from "./Breadcrumb";
export { Breakpoint } from "./Breakpoint"; export { Breakpoint, breakpoints, breakpointObserver } from "./Breakpoint";
export { Button, ButtonSkeleton, ButtonSet } from "./Button"; export { Button, ButtonSkeleton, ButtonSet } from "./Button";
export { Checkbox, CheckboxSkeleton } from "./Checkbox"; export { Checkbox, CheckboxSkeleton } from "./Checkbox";
export { ContentSwitcher, Switch } from "./ContentSwitcher"; export { ContentSwitcher, Switch } from "./ContentSwitcher";

View file

@ -1,10 +1,6 @@
/// <reference types="svelte" /> /// <reference types="svelte" />
import { SvelteComponentTyped } from "svelte"; import { SvelteComponentTyped } from "svelte";
export type BreakpointSize = "sm" | "md" | "lg" | "xlg" | "max";
export type BreakpointValue = 320 | 672 | 1056 | 1312 | 1584;
export interface BreakpointProps { export interface BreakpointProps {
/** /**
* Determine the current Carbon grid breakpoint size * Determine the current Carbon grid breakpoint size
@ -22,15 +18,10 @@ export interface BreakpointProps {
export default class Breakpoint extends SvelteComponentTyped< export default class Breakpoint extends SvelteComponentTyped<
BreakpointProps, BreakpointProps,
{ {
match: CustomEvent<{ change: CustomEvent<{
size: BreakpointSize; size: BreakpointSize;
breakpointValue: BreakpointValue; breakpointValue: BreakpointValue;
}>; }>;
}, },
{ default: { size: BreakpointSize; sizes: Record<BreakpointSize, boolean> } } { default: { size: BreakpointSize; sizes: Record<BreakpointSize, boolean> } }
> { > {}
/**
* Reference the Carbon grid breakpoints
*/
breakpoints: Record<BreakpointSize, BreakpointValue>;
}

2
types/index.d.ts vendored
View file

@ -6,6 +6,8 @@ export { default as Breadcrumb } from "./Breadcrumb/Breadcrumb.svelte";
export { default as BreadcrumbItem } from "./Breadcrumb/BreadcrumbItem.svelte"; export { default as BreadcrumbItem } from "./Breadcrumb/BreadcrumbItem.svelte";
export { default as BreadcrumbSkeleton } from "./Breadcrumb/BreadcrumbSkeleton.svelte"; export { default as BreadcrumbSkeleton } from "./Breadcrumb/BreadcrumbSkeleton.svelte";
export { default as Breakpoint } from "./Breakpoint/Breakpoint.svelte"; export { default as Breakpoint } from "./Breakpoint/Breakpoint.svelte";
export { default as breakpoints } from "./Breakpoint";
export { default as breakpointObserver } from "./Breakpoint";
export { default as Button } from "./Button/Button.svelte"; export { default as Button } from "./Button/Button.svelte";
export { default as ButtonSkeleton } from "./Button/ButtonSkeleton.svelte"; export { default as ButtonSkeleton } from "./Button/ButtonSkeleton.svelte";
export { default as ButtonSet } from "./Button/ButtonSet.svelte"; export { default as ButtonSet } from "./Button/ButtonSet.svelte";