mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-14 18:01:06 +00:00
feat(portal): support portal
This commit is contained in:
parent
0ea3d9351e
commit
1ddd9ba1a5
14 changed files with 240 additions and 2 deletions
|
@ -1,6 +1,6 @@
|
||||||
# Component Index
|
# Component Index
|
||||||
|
|
||||||
> 165 components exported from carbon-components-svelte@0.89.2.
|
> 167 components exported from carbon-components-svelte@0.89.2.
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@
|
||||||
- [`FileUploaderItem`](#fileuploaderitem)
|
- [`FileUploaderItem`](#fileuploaderitem)
|
||||||
- [`FileUploaderSkeleton`](#fileuploaderskeleton)
|
- [`FileUploaderSkeleton`](#fileuploaderskeleton)
|
||||||
- [`Filename`](#filename)
|
- [`Filename`](#filename)
|
||||||
|
- [`FloatingPortal`](#floatingportal)
|
||||||
- [`FluidForm`](#fluidform)
|
- [`FluidForm`](#fluidform)
|
||||||
- [`Form`](#form)
|
- [`Form`](#form)
|
||||||
- [`FormGroup`](#formgroup)
|
- [`FormGroup`](#formgroup)
|
||||||
|
@ -95,6 +96,7 @@
|
||||||
- [`PaginationSkeleton`](#paginationskeleton)
|
- [`PaginationSkeleton`](#paginationskeleton)
|
||||||
- [`PasswordInput`](#passwordinput)
|
- [`PasswordInput`](#passwordinput)
|
||||||
- [`Popover`](#popover)
|
- [`Popover`](#popover)
|
||||||
|
- [`Portal`](#portal)
|
||||||
- [`ProgressBar`](#progressbar)
|
- [`ProgressBar`](#progressbar)
|
||||||
- [`ProgressIndicator`](#progressindicator)
|
- [`ProgressIndicator`](#progressindicator)
|
||||||
- [`ProgressIndicatorSkeleton`](#progressindicatorskeleton)
|
- [`ProgressIndicatorSkeleton`](#progressindicatorskeleton)
|
||||||
|
@ -1435,6 +1437,20 @@ None.
|
||||||
| click | forwarded | -- |
|
| click | forwarded | -- |
|
||||||
| keydown | forwarded | -- |
|
| keydown | forwarded | -- |
|
||||||
|
|
||||||
|
## `FloatingPortal`
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Slots
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
## `FluidForm`
|
## `FluidForm`
|
||||||
|
|
||||||
### Props
|
### Props
|
||||||
|
@ -2835,6 +2851,22 @@ None.
|
||||||
| :------------ | :--------- | :------------------------------------ |
|
| :------------ | :--------- | :------------------------------------ |
|
||||||
| click:outside | dispatched | <code>{ target: HTMLElement; }</code> |
|
| click:outside | dispatched | <code>{ target: HTMLElement; }</code> |
|
||||||
|
|
||||||
|
## `Portal`
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
### Slots
|
||||||
|
|
||||||
|
| Slot name | Default | Props | Fallback |
|
||||||
|
| :-------- | :------ | :---- | :------- |
|
||||||
|
| -- | Yes | -- | -- |
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
## `ProgressBar`
|
## `ProgressBar`
|
||||||
|
|
||||||
### Props
|
### Props
|
||||||
|
|
1
docs/package-lock.json
generated
1
docs/package-lock.json
generated
|
@ -30,6 +30,7 @@
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@floating-ui/dom": "^1.6.13",
|
||||||
"@ibm/telemetry-js": "^1.5.0",
|
"@ibm/telemetry-js": "^1.5.0",
|
||||||
"flatpickr": "4.6.9"
|
"flatpickr": "4.6.9"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"total": 165,
|
"total": 167,
|
||||||
"components": [
|
"components": [
|
||||||
{
|
{
|
||||||
"moduleName": "Accordion",
|
"moduleName": "Accordion",
|
||||||
|
@ -5241,6 +5241,16 @@
|
||||||
"name": "div | button | svg"
|
"name": "div | button | svg"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"moduleName": "FloatingPortal",
|
||||||
|
"filePath": "src/Portal/FloatingPortal.svelte",
|
||||||
|
"props": [],
|
||||||
|
"moduleExports": [],
|
||||||
|
"slots": [],
|
||||||
|
"events": [],
|
||||||
|
"typedefs": [],
|
||||||
|
"generics": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"moduleName": "FluidForm",
|
"moduleName": "FluidForm",
|
||||||
"filePath": "src/FluidForm/FluidForm.svelte",
|
"filePath": "src/FluidForm/FluidForm.svelte",
|
||||||
|
@ -10851,6 +10861,22 @@
|
||||||
"name": "div"
|
"name": "div"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"moduleName": "Portal",
|
||||||
|
"filePath": "src/Portal/Portal.svelte",
|
||||||
|
"props": [],
|
||||||
|
"moduleExports": [],
|
||||||
|
"slots": [
|
||||||
|
{
|
||||||
|
"name": "__default__",
|
||||||
|
"default": true,
|
||||||
|
"slot_props": "{}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"events": [],
|
||||||
|
"typedefs": [],
|
||||||
|
"generics": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"moduleName": "ProgressBar",
|
"moduleName": "ProgressBar",
|
||||||
"filePath": "src/ProgressBar/ProgressBar.svelte",
|
"filePath": "src/ProgressBar/ProgressBar.svelte",
|
||||||
|
|
8
docs/src/pages/components/Portal.svx
Normal file
8
docs/src/pages/components/Portal.svx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import { Portal } from "carbon-components-svelte";
|
||||||
|
import Preview from "../../components/Preview.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
## Default
|
||||||
|
|
||||||
|
<FileSource src="/framed/Portal/BasicPortal" />
|
6
docs/src/pages/framed/Portal/BasicPortal.svelte
Normal file
6
docs/src/pages/framed/Portal/BasicPortal.svelte
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<script>
|
||||||
|
import { Portal } from "carbon-components-svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Portal>Hello world</Portal>
|
||||||
|
<Portal>Another portal</Portal>
|
26
package-lock.json
generated
26
package-lock.json
generated
|
@ -10,6 +10,7 @@
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@floating-ui/dom": "^1.6.13",
|
||||||
"@ibm/telemetry-js": "^1.5.0",
|
"@ibm/telemetry-js": "^1.5.0",
|
||||||
"flatpickr": "4.6.9"
|
"flatpickr": "4.6.9"
|
||||||
},
|
},
|
||||||
|
@ -623,6 +624,31 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@floating-ui/core": {
|
||||||
|
"version": "1.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz",
|
||||||
|
"integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/utils": "^0.2.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/dom": {
|
||||||
|
"version": "1.6.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz",
|
||||||
|
"integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/core": "^1.6.0",
|
||||||
|
"@floating-ui/utils": "^0.2.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/utils": {
|
||||||
|
"version": "0.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
|
||||||
|
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@hutson/parse-repository-url": {
|
"node_modules/@hutson/parse-repository-url": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz",
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
"release": "standard-version && npm run build:docs"
|
"release": "standard-version && npm run build:docs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@floating-ui/dom": "^1.6.13",
|
||||||
"@ibm/telemetry-js": "^1.5.0",
|
"@ibm/telemetry-js": "^1.5.0",
|
||||||
"flatpickr": "4.6.9"
|
"flatpickr": "4.6.9"
|
||||||
},
|
},
|
||||||
|
|
62
src/Portal/FloatingPortal.svelte
Normal file
62
src/Portal/FloatingPortal.svelte
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { computePosition, autoUpdate } from "@floating-ui/dom";
|
||||||
|
import Portal from "./Portal.svelte";
|
||||||
|
|
||||||
|
/** @type {null | HTMLButtonElement} */
|
||||||
|
let button = null;
|
||||||
|
|
||||||
|
/** @type {null | HTMLDivElement} */
|
||||||
|
let tooltip = null;
|
||||||
|
|
||||||
|
let togglePortal = false;
|
||||||
|
let padding = 10;
|
||||||
|
|
||||||
|
/** @type {null | ReturnType<typeof autoUpdate>} */
|
||||||
|
let cleanup = null;
|
||||||
|
|
||||||
|
function updatePosition() {
|
||||||
|
if (!button || !tooltip) return;
|
||||||
|
computePosition(button, tooltip).then(({ x, y }) => {
|
||||||
|
if (!tooltip) return;
|
||||||
|
Object.assign(tooltip.style, {
|
||||||
|
left: `${x}px`,
|
||||||
|
top: `${y}px`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
updatePosition();
|
||||||
|
|
||||||
|
if (button && tooltip) {
|
||||||
|
cleanup = autoUpdate(button, tooltip, updatePosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
return cleanup?.();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
style="padding: {padding}px"
|
||||||
|
bind:this={button}
|
||||||
|
on:click={() => (padding += 2)}
|
||||||
|
>
|
||||||
|
Click to increase padding
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
togglePortal = !togglePortal;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Toggle portal
|
||||||
|
</button>
|
||||||
|
{#if togglePortal}
|
||||||
|
<Portal>
|
||||||
|
<div bind:this={tooltip}>Floating menu</div>
|
||||||
|
</Portal>
|
||||||
|
{/if}
|
53
src/Portal/Portal.svelte
Normal file
53
src/Portal/Portal.svelte
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<script context="module">
|
||||||
|
/** @type {HTMLDivElement | null} */
|
||||||
|
let portalContainer = null;
|
||||||
|
|
||||||
|
let instances = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates or returns the shared portal container
|
||||||
|
* @returns {HTMLDivElement}
|
||||||
|
*/
|
||||||
|
function getPortalContainer() {
|
||||||
|
if (!portalContainer && typeof document !== "undefined") {
|
||||||
|
portalContainer = document.createElement("div");
|
||||||
|
portalContainer.setAttribute("data-portal", "");
|
||||||
|
document.body.appendChild(portalContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return portalContainer;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
/** @type {null | HTMLDivElement} */
|
||||||
|
let portal = null;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
instances++;
|
||||||
|
const container = getPortalContainer();
|
||||||
|
|
||||||
|
if (portal && container) {
|
||||||
|
container.appendChild(portal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
instances--;
|
||||||
|
|
||||||
|
if (portal?.parentNode) {
|
||||||
|
portal.parentNode.removeChild(portal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instances === 0 && portalContainer?.parentNode) {
|
||||||
|
portalContainer.parentNode.removeChild(portalContainer);
|
||||||
|
portalContainer = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={portal}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
2
src/Portal/index.js
Normal file
2
src/Portal/index.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as Portal } from "./Portal.svelte";
|
||||||
|
export { default as FloatingPortal } from "./FloatingPortal.svelte";
|
|
@ -91,6 +91,7 @@ export {
|
||||||
ProgressIndicatorSkeleton,
|
ProgressIndicatorSkeleton,
|
||||||
ProgressStep,
|
ProgressStep,
|
||||||
} from "./ProgressIndicator";
|
} from "./ProgressIndicator";
|
||||||
|
export { Portal, FloatingPortal } from "./Portal";
|
||||||
export { RadioButton, RadioButtonSkeleton } from "./RadioButton";
|
export { RadioButton, RadioButtonSkeleton } from "./RadioButton";
|
||||||
export { RadioButtonGroup } from "./RadioButtonGroup";
|
export { RadioButtonGroup } from "./RadioButtonGroup";
|
||||||
export { RecursiveList } from "./RecursiveList";
|
export { RecursiveList } from "./RecursiveList";
|
||||||
|
|
9
types/Portal/FloatingPortal.svelte.d.ts
vendored
Normal file
9
types/Portal/FloatingPortal.svelte.d.ts
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import type { SvelteComponentTyped } from "svelte";
|
||||||
|
|
||||||
|
export type FloatingPortalProps = {};
|
||||||
|
|
||||||
|
export default class FloatingPortal extends SvelteComponentTyped<
|
||||||
|
FloatingPortalProps,
|
||||||
|
Record<string, any>,
|
||||||
|
{}
|
||||||
|
> {}
|
9
types/Portal/Portal.svelte.d.ts
vendored
Normal file
9
types/Portal/Portal.svelte.d.ts
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import type { SvelteComponentTyped } from "svelte";
|
||||||
|
|
||||||
|
export type PortalProps = {};
|
||||||
|
|
||||||
|
export default class Portal extends SvelteComponentTyped<
|
||||||
|
PortalProps,
|
||||||
|
Record<string, any>,
|
||||||
|
{ default: {} }
|
||||||
|
> {}
|
2
types/index.d.ts
vendored
2
types/index.d.ts
vendored
|
@ -95,6 +95,8 @@ export { default as ProgressBar } from "./ProgressBar/ProgressBar.svelte";
|
||||||
export { default as ProgressIndicator } from "./ProgressIndicator/ProgressIndicator.svelte";
|
export { default as ProgressIndicator } from "./ProgressIndicator/ProgressIndicator.svelte";
|
||||||
export { default as ProgressIndicatorSkeleton } from "./ProgressIndicator/ProgressIndicatorSkeleton.svelte";
|
export { default as ProgressIndicatorSkeleton } from "./ProgressIndicator/ProgressIndicatorSkeleton.svelte";
|
||||||
export { default as ProgressStep } from "./ProgressIndicator/ProgressStep.svelte";
|
export { default as ProgressStep } from "./ProgressIndicator/ProgressStep.svelte";
|
||||||
|
export { default as Portal } from "./Portal/Portal.svelte";
|
||||||
|
export { default as FloatingPortal } from "./Portal/FloatingPortal.svelte";
|
||||||
export { default as RadioButton } from "./RadioButton/RadioButton.svelte";
|
export { default as RadioButton } from "./RadioButton/RadioButton.svelte";
|
||||||
export { default as RadioButtonSkeleton } from "./RadioButton/RadioButtonSkeleton.svelte";
|
export { default as RadioButtonSkeleton } from "./RadioButton/RadioButtonSkeleton.svelte";
|
||||||
export { default as RadioButtonGroup } from "./RadioButtonGroup/RadioButtonGroup.svelte";
|
export { default as RadioButtonGroup } from "./RadioButtonGroup/RadioButtonGroup.svelte";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue