mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-14 18:01:06 +00:00
breaking(breakpoint): add breakpointObserver store (#1092)
* breaking: re-name event "on:match" to "on:change" in `Breakpoint.svelte` * feat: add `breakpointObserver` read-only store * refactor: export breakpoint constants from `breakpoints.js`
This commit is contained in:
parent
bbd2cbe62c
commit
5de0d9a357
14 changed files with 180 additions and 101 deletions
|
@ -358,11 +358,10 @@ export type BreakpointValue = 320 | 672 | 1056 | 1312 | 1584;
|
|||
|
||||
### Props
|
||||
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| size | <code>let</code> | Yes | <code>BreakpointSize</code> | <code>undefined</code> | Determine the current Carbon grid breakpoint size |
|
||||
|
||||
### Slots
|
||||
|
||||
|
@ -374,7 +373,7 @@ export type BreakpointValue = 320 | 672 | 1056 | 1312 | 1584;
|
|||
|
||||
| Event name | Type | Detail |
|
||||
| :--------- | :--------- | :----------------------------------------------------------------------- |
|
||||
| match | dispatched | <code>{ size: BreakpointSize; breakpointValue: BreakpointValue; }</code> |
|
||||
| change | dispatched | <code>{ size: BreakpointSize; breakpointValue: BreakpointValue; }</code> |
|
||||
|
||||
## `Button`
|
||||
|
||||
|
|
|
@ -387,17 +387,6 @@
|
|||
"isFunctionDeclaration": false,
|
||||
"constant": false,
|
||||
"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": [
|
||||
|
@ -410,7 +399,7 @@
|
|||
"events": [
|
||||
{
|
||||
"type": "dispatched",
|
||||
"name": "match",
|
||||
"name": "change",
|
||||
"detail": "{ size: BreakpointSize; breakpointValue: BreakpointValue; }"
|
||||
}
|
||||
],
|
||||
|
|
|
@ -22,6 +22,14 @@ This utility component uses the [Window.matchMedia API](https://developer.mozill
|
|||
|
||||
Bind to the `size` prop to determine the current breakpoint size. Possible values include: `"sm" | "md" | "lg" | "xlg" | "max"`.
|
||||
|
||||
The `"on:match"` event will fire only when a breakpoint change event occurs (e.g., the browser width is resized).
|
||||
The `"on:change"` event will fire when the size is initially determined and when a breakpoint change event occurs (e.g., the browser width is resized).
|
||||
|
||||
<FileSource src="/framed/Breakpoint/Breakpoint" />
|
||||
<FileSource src="/framed/Breakpoint/Breakpoint" />
|
||||
|
||||
### Store and Breakpoint Values
|
||||
|
||||
As an alternative to the component, `breakpointObserver` can be used to get the current size as a Svelte store. The store has two additional functions which create derived stores that return a `boolean` indicating whether the size is smaller/larger than a certain breakpoint.
|
||||
|
||||
There also exists a `breakpoints` dictionary mapping from `BreakpointSize` to `BreakpointValue`.
|
||||
|
||||
<FileSource src="/framed/Breakpoint/BreakpointObserver" />
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
let events = [];
|
||||
</script>
|
||||
|
||||
<Breakpoint bind:size on:match="{(e) => (events = [...events, e.detail])}" />
|
||||
<Breakpoint bind:size on:change="{(e) => (events = [...events, e.detail])}" />
|
||||
|
||||
<p>Resize the width of your browser.</p>
|
||||
<h6>Breakpoint size</h6>
|
||||
<h1>{size}</h1>
|
||||
|
||||
<h6>on:match</h6>
|
||||
<h6>on:change</h6>
|
||||
<pre>
|
||||
{JSON.stringify(events, null, 2)}
|
||||
</pre>
|
||||
|
|
15
docs/src/pages/framed/Breakpoint/BreakpointObserver.svelte
Normal file
15
docs/src/pages/framed/Breakpoint/BreakpointObserver.svelte
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
import {
|
||||
breakpointObserver,
|
||||
breakpoints,
|
||||
} from "carbon-components-svelte/src/Breakpoint";
|
||||
|
||||
const size = breakpointObserver();
|
||||
const smaller = size.smallerThan("md");
|
||||
const larger = size.largerThan("md");
|
||||
</script>
|
||||
|
||||
<p>Current breakpoint size: {$size}</p>
|
||||
<p>Current breakpoint value: {breakpoints[$size]}px</p>
|
||||
<p>Smaller than medium: {$smaller}</p>
|
||||
<p>Larger than medium: {$larger}</p>
|
|
@ -2,7 +2,7 @@
|
|||
/**
|
||||
* @typedef {"sm" | "md" | "lg" | "xlg" | "max"} BreakpointSize
|
||||
* @typedef {320 | 672 | 1056 | 1312 | 1584} BreakpointValue
|
||||
* @event {{ size: BreakpointSize; breakpointValue: BreakpointValue; }} match
|
||||
* @event {{ size: BreakpointSize; breakpointValue: BreakpointValue; }} change
|
||||
* @slot {{ size: BreakpointSize; sizes: Record<BreakpointSize, boolean>; }}
|
||||
*/
|
||||
|
||||
|
@ -24,77 +24,23 @@
|
|||
max: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Reference the Carbon grid breakpoints
|
||||
* @type {Record<BreakpointSize, BreakpointValue>}
|
||||
*/
|
||||
export const breakpoints = {
|
||||
sm: 320,
|
||||
md: 672,
|
||||
lg: 1056,
|
||||
xlg: 1312,
|
||||
max: 1584,
|
||||
};
|
||||
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { breakpointObserver } from "./breakpointObserver";
|
||||
import { breakpoints } from "./breakpoints";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const observer = breakpointObserver();
|
||||
|
||||
onMount(() => {
|
||||
const width = window.innerWidth;
|
||||
|
||||
if (width > breakpoints.max) size = "max";
|
||||
else if (width < breakpoints.md) size = "sm";
|
||||
else if (width >= breakpoints.md && width <= breakpoints.lg) size = "md";
|
||||
else if (width >= breakpoints.lg && width <= breakpoints.xlg) size = "lg";
|
||||
else if (width >= breakpoints.xlg && width <= breakpoints.max) size = "xlg";
|
||||
|
||||
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 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";
|
||||
}
|
||||
$: size = $observer;
|
||||
$: sizes = {
|
||||
sm: size == "sm",
|
||||
md: size == "md",
|
||||
lg: size == "lg",
|
||||
xlg: size == "xlg",
|
||||
max: size == "max",
|
||||
};
|
||||
$: if (size != undefined)
|
||||
dispatch("change", { size, breakpointValue: breakpoints[size] });
|
||||
</script>
|
||||
|
||||
<slot size="{size}" sizes="{sizes}" />
|
||||
|
|
22
src/Breakpoint/breakpointObserver.d.ts
vendored
Normal file
22
src/Breakpoint/breakpointObserver.d.ts
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
import type { Readable, Subscriber, Unsubscriber } from "svelte/store";
|
||||
import type { BreakpointSize, BreakpointValue } from "./breakpoints";
|
||||
|
||||
/**
|
||||
* Creates a readable store that returns the current breakpoint size.
|
||||
* It also provides functions for creating derived stores used to do comparisons.
|
||||
*/
|
||||
export function breakpointObserver(): {
|
||||
subscribe: (this: void, run: Subscriber<any>, invalidate?: (value?: any) => void) => Unsubscriber;
|
||||
/**
|
||||
* Returns a store readable store that returns whether the current
|
||||
* breakpoint is smaller than {@link size}.
|
||||
* @param {BreakpointSize} size Size to compare against.
|
||||
*/
|
||||
smallerThan: (size: BreakpointSize) => Readable<boolean>;
|
||||
/**
|
||||
* Returns a store readable store that returns whether the current
|
||||
* breakpoint is larger than {@link size}.
|
||||
* @param {BreakpointSize} size Size to compare against.
|
||||
*/
|
||||
largerThan: (size: BreakpointSize) => Readable<boolean>;
|
||||
};
|
80
src/Breakpoint/breakpointObserver.js
Normal file
80
src/Breakpoint/breakpointObserver.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
import { onMount } from "svelte";
|
||||
import { derived, writable } from "svelte/store";
|
||||
import { breakpoints } from "./breakpoints";
|
||||
|
||||
/**
|
||||
* Creates a readable store that returns the current breakpoint size.
|
||||
* It also provides functions for creating derived stores used to do comparisons.
|
||||
*/
|
||||
export function breakpointObserver() {
|
||||
const store = writable(undefined);
|
||||
|
||||
onMount(() => {
|
||||
/** @type {Record<import("./breakpoints").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 is smaller than {@link size}.
|
||||
* @param {import("./breakpoints").BreakpointSize} size Size to compare against.
|
||||
*/
|
||||
smallerThan: (size) => {
|
||||
checkSizeValid(size);
|
||||
return derived(store, ($size) => breakpoints[$size] < breakpoints[size]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a store readable store that returns whether the current
|
||||
* breakpoint is larger than {@link size}.
|
||||
* @param {import("./breakpoints").BreakpointSize} size Size to compare against.
|
||||
*/
|
||||
largerThan: (size) => {
|
||||
checkSizeValid(size);
|
||||
return derived(store, ($size) => breakpoints[$size] > breakpoints[size]);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function checkSizeValid(size) {
|
||||
if (size in breakpoints == false)
|
||||
throw new Error(`"${size}" is not a valid breakpoint size.`);
|
||||
}
|
9
src/Breakpoint/breakpoints.d.ts
vendored
Normal file
9
src/Breakpoint/breakpoints.d.ts
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Pixel sizes of Carbon grid breakpoints.
|
||||
* @type {Record<BreakpointSize, BreakpointValue>}
|
||||
*/
|
||||
export const breakpoints: Record<BreakpointSize, BreakpointValue>;
|
||||
|
||||
export type BreakpointSize = "sm" | "md" | "lg" | "xlg" | "max";
|
||||
|
||||
export type BreakpointValue = 320 | 672 | 1056 | 1312 | 1584;
|
11
src/Breakpoint/breakpoints.js
Normal file
11
src/Breakpoint/breakpoints.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* Pixel sizes of Carbon grid breakpoints.
|
||||
* @type {Record<import("./breakpoints").BreakpointSize, BreakpointValue>}
|
||||
*/
|
||||
export const breakpoints = Object.freeze({
|
||||
sm: 320,
|
||||
md: 672,
|
||||
lg: 1056,
|
||||
xlg: 1312,
|
||||
max: 1584,
|
||||
});
|
3
src/Breakpoint/index.d.ts
vendored
Normal file
3
src/Breakpoint/index.d.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
export { default as Breakpoint } from "./Breakpoint.svelte";
|
||||
export { breakpointObserver } from "./breakpointObserver";
|
||||
export { breakpoints } from "./breakpoints";
|
|
@ -1 +1,3 @@
|
|||
export { default as Breakpoint } from "./Breakpoint.svelte";
|
||||
export { breakpointObserver } from "./breakpointObserver";
|
||||
export { breakpoints } from "./breakpoints";
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<script lang="ts">
|
||||
import { Breakpoint } from "../types";
|
||||
import type { BreakpointSize } from "../types/Breakpoint/Breakpoint.svelte";
|
||||
import type { BreakpointProps } from "../types/Breakpoint/Breakpoint.svelte";
|
||||
|
||||
let size: BreakpointSize;
|
||||
let size: BreakpointProps["size"];
|
||||
</script>
|
||||
|
||||
<Breakpoint
|
||||
bind:size
|
||||
let:size="{currentSize}"
|
||||
on:match="{(e) => {
|
||||
on:change="{(e) => {
|
||||
console.log(e.detail);
|
||||
}}"
|
||||
>
|
||||
|
|
9
types/Breakpoint/Breakpoint.svelte.d.ts
vendored
9
types/Breakpoint/Breakpoint.svelte.d.ts
vendored
|
@ -22,15 +22,10 @@ export interface BreakpointProps {
|
|||
export default class Breakpoint extends SvelteComponentTyped<
|
||||
BreakpointProps,
|
||||
{
|
||||
match: CustomEvent<{
|
||||
change: CustomEvent<{
|
||||
size: BreakpointSize;
|
||||
breakpointValue: BreakpointValue;
|
||||
}>;
|
||||
},
|
||||
{ default: { size: BreakpointSize; sizes: Record<BreakpointSize, boolean> } }
|
||||
> {
|
||||
/**
|
||||
* Reference the Carbon grid breakpoints
|
||||
*/
|
||||
breakpoints: Record<BreakpointSize, BreakpointValue>;
|
||||
}
|
||||
> {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue