├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── prettier.config.js ├── src ├── app.css ├── app.html ├── lib │ ├── Counter.svelte │ ├── IconMenu.svelte │ ├── Menu.svelte │ └── count.svelte.ts ├── package │ ├── OutClick.svelte │ ├── OutClickEvent.ts │ ├── castArray.ts │ ├── index.ts │ └── types.ts └── routes │ ├── +layout.svelte │ ├── +page.svelte │ ├── exclude │ └── +page.svelte │ └── half-click │ └── +page.svelte ├── static ├── Exo2-VariableFont_wght.woff2 └── favicon.png ├── svelte.config.js ├── tailwind.config.ts ├── tsconfig.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules/ 3 | /.svelte-kit/ 4 | /.vercel/ 5 | /dist/ 6 | vite.config.ts.timestamp-* 7 | pnpm-lock.yaml 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CHANGELOG 3 | --- 4 | 5 | > [!IMPORTANT] 6 | > Restart your app after updating the package. 7 | 8 | ## 4.1.0 9 | 10 | - [feature] Added `tag` prop and rest props type suggestion support with generic type support based on the `tag` prop. 11 | 12 | ## 4.0.1 13 | 14 | - Fixed `"svelte"` `"peerDependencies"` version by changing it to `">= 5"`. 15 | 16 | ## 4.0.0 17 | 18 | > [!IMPORTANT] 19 | > There are breaking changes in this version. 20 | 21 | - [breaking] Use `e.target` instead of `e.detail.target`. 22 | - [breaking] `OutClickEvent` now returns `pointerdown` or `pointerup` or `keydown`, depending on how the outclick event is fired. 23 | - [breaking] Also, take a look at the migration guide below. 24 | - Exports `OutClickEvent` type. 25 | 26 | ### Migration guide 27 | 28 | Before: 29 | 30 | ```svelte 31 | 34 | ``` 35 | 36 | After: 37 | 38 | ```svelte 39 | 42 | ``` 43 | 44 | --- 45 | 46 | Before: 47 | 48 | ```svelte 49 | {}}> 50 | ``` 51 | 52 | After: 53 | 54 | ```svelte 55 | {}}> 56 | ``` 57 | 58 | ## 3.7.1 59 | 60 | - [feature] Added `e.detail.target` property. 61 | - [breaking] Requires version `4.2.12` or higher. 62 | 63 | ## 3.7.0 64 | 65 | - Compatible with SvelteKit version `2.0.0`. 66 | 67 | ## 3.6.2 68 | 69 | - Now it's compatible with Svelte 5. 70 | 71 | ## 3.6.1 72 | 73 | - Fixed some issues related to props types. 74 | 75 | ## 3.6.0 76 | 77 | - Removed `e.detail` because it wasn't needed. 78 | 79 | ## 3.5.0 80 | 81 | - Please update to version `3.5.0` if you were using the `3.4.0` version. 82 | 83 | ## 3.4.0 84 | 85 | - Added TypeScript support. 86 | 87 | ## 3.3.1 88 | 89 | - Fixed some JSDocs data types. 90 | 91 | ## 3.3.0 92 | 93 | - [breaking] Renamed `fullClick` to `halfClick` (default: `false`). 94 | - [breaking] Renamed `excludeByDomNode` to `excludeElements`. 95 | - [breaking] Renamed `excludeByQuerySelector` to `excludeQuerySelectorAll`. 96 | - Now you can set the `excludeElements` prop to the element itself instead of wrapping it with an array. 97 | - Now the `excludeQuerySelectorAll` prop works the same as the JavaScript [`querySelectorAll`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) method. It can contain values like `"#element1, .element2"` and `['#element1', '.element2']`. 98 | - Added custom tag support. Now you can add a prop called `tag` to your `OutClick` component and change the wrapper tag. Added with the help of `svelte:element`. 99 | - Now you can add custom attributes to render on the wrapper. Added with the help of `$$restProps`. 100 | 101 | ## 3.2.0 102 | 103 | - [breaking] Removed `useWrapper` prop, because it was unnecessary. 104 | - [breaking] Removed the default class `outclick` from the wrapper. 105 | - [breaking] Renamed `excludeByDOMNode` to `excludeByDomNode` 106 | - [breaking] Added new `fullClick` prop. Now clicking outside requires `pointerdown` and `pointerup` to fire at outside of your element. Set it to `false` so `pointerdown` can fire the `outclick` event on its own. 107 | - Fix empty `class` attribute showing up when not using a class. 108 | - Fix empty `style` attribute showing up when using the `class` prop. 109 | 110 | ## 3.1.0 111 | 112 | - Changed `on:mousedown` to `on:pointerdown` and fixed [this issue](https://github.com/babakfp/svelte-outclick/issues/6). 113 | 114 | ## 3.0.1 115 | 116 | - Removed `ROADMAP.todo`. 117 | - Fixed typo in `README.md`. 118 | - Rewrite the description in `README.md`. 119 | 120 | ## 3.0.0 121 | 122 | - Remplaced `exclude` prop with `excludeByDOMNode` and `excludeByQuerySelector`. 123 | - Renamed `.outclick-wrapper` component wrapper class to `.outclick`. 124 | - Using `on:mousedown` and `on:keydown` instead of `on:click`. 125 | - Removed `useMousedown` and `useKeydown` props. 126 | - If you use `class` prop, `display: contents` will be removed by default. 127 | 128 | ## 2.6.5 129 | 130 | - Added `useMousedown` and `useKeydown` props. 131 | - Fixed [this issue](https://github.com/babakfp/svelte-outclick/issues/4). 132 | 133 | ## 2.5.4 134 | 135 | - Replaced `on:click` with `on:mousedown` and fixed [this issue](https://github.com/babakfp/svelte-outclick/issues/4). 136 | 137 | ## 2.4.3 138 | 139 | - The `exclude` prop now accepts HTML `class` and `id` instead of DOM nodes. 140 | 141 | ## 2.3.1 142 | 143 | - Renamed dispatch `detail.self` to `detail.wrapper`. 144 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 babakfp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Svelte OutClick 2 | 3 | [![NPM](https://img.shields.io/npm/v/svelte-outclick?style=for-the-badge&label=NPM&color=%23cb0000)](https://www.npmjs.com/package/svelte-outclick) 4 | 5 | A Svelte component that allows you to listen to the clicks that happen outside of an element. 6 | 7 | - 📺 [Demo](https://svelte-outclick.vercel.app) 8 | - 🪵 [CHANGELOG](https://github.com/babakfp/svelte-outclick/blob/main/README.md) 9 | 10 | > [!IMPORTANT] 11 | > The latest version of this package is not compatible with Svelte 4. Please use the version `3.7.1` for Svelte 4 compatibility. 12 | 13 | --- 14 | 15 | `onOutClick` 16 | 17 | A Svelte component that allows you to listen to the clicks that happen outside of an element. 18 | 19 | Why choose this over the other packages? 20 | 21 | - [No extra wrapper](#no-extra-wrapper) 22 | - Supports [`class`](#class-prop) prop 23 | - [Exclude elements](#excluding-elements) from triggering the event 24 | - [Half click](#halfclick) support 25 | - TypeScript support 26 | 27 | ## Install 28 | 29 | Please check out the [**CHANGELOG**](svelte-outclick/changelog) before updating to the newest version. Restart your app after the update. 30 | 31 | ```bash 32 | pnpm add -D svelte-outclick 33 | ``` 34 | 35 | ## How it works 36 | 37 | It works the same as the Javascript click event. A few events are attached to the window and it checks whether the event target is contained within the element. If the element didn't contain the event target, it means the click happened outside of the element. 38 | 39 | 40 | ```svelte 41 | 45 | 46 | count += 1} 48 | > 49 | {count} times clicked outside 50 | 51 | ``` 52 | 53 | ## Props 54 | 55 | ### Excluding elements 56 | 57 | You may want to exclude some elements from triggering the event. For example, a button that opens a popup must be excluded, so the popup doesn't get closed immediately after you open it. Since the button that triggers the popup is located outside of the popup itself, in any time clicking on it will trigger the event. 58 | 59 | #### `excludeElements` 60 | 61 | - Type: `HTMLElement | HTMLElement[]` 62 | 63 | This prop expects HTML elements. Clicks on those elements (and their children) will be ignored. Learn about [`bind:this`](https://svelte.dev/tutorial/bind-this). 64 | 65 | 66 | ```svelte 67 | 72 | 73 | count += 1} 75 | excludeElements={excludedElement} 76 | > 77 | {count} times clicked outside 78 | 79 | 80 |
81 | This doesn't trigger outclick 82 |
83 | ``` 84 | 85 | This prop can receive a single element or an array of elements. 86 | 87 | #### `excludeQuerySelectorAll` 88 | 89 | - Type: `string` 90 | 91 | This prop expects a string of css selectors. Clicks on those elements (and their children) will be ignored. 92 | 93 | 94 | ```svelte 95 | 99 | 100 | count += 1} 102 | excludeQuerySelectorAll=".excluded-element" 103 | > 104 | {count} times clicked outside 105 | 106 | 107 |
108 | This doesn't trigger outclick 109 |
110 | ``` 111 | 112 | This prop works the same as the `querySelectorAll` method. It can contain any valid CSS selectors, for example: `"#element1, .element2"`. 113 | 114 | ### `class` prop 115 | 116 | - Type: `string` 117 | 118 | This is equivalent to the CSS class attribute. You can seamlessly utilize tools such as Tailwind CSS by adding your classes in the usual manner, without encountering any issues. 119 | 120 | 121 | ```svelte 122 | 126 | 127 |
128 | count += 1} 131 | > 132 | {count} times clicked outside 133 | 134 |
135 | 136 | 142 | ``` 143 | 144 | ### `includeSelf` 145 | 146 | - Type: `boolean` 147 | - Default: `false` 148 | 149 | For example, if you want to close the dropdown when you click on any of its items, set the value of this option to `true`, so a self-click can trigger the event. 150 | 151 | ### `halfClick` 152 | 153 | - Type: `boolean` 154 | - Default: `true` 155 | 156 | If `true`, `pointerdown` can solely determine the click outside. If `false`, outclick will happen when `pointerdown` and `pointerup` events happen after each other, outside of the element. 157 | 158 | ### `tag` 159 | 160 | - Type: `string` 161 | - Default: `"div"` 162 | 163 | You can change the tag name of the wrapper element. 164 | 165 | ### Custom attributes 166 | 167 | You can add any custom attributes to the wrapper element. 168 | 169 | ```svelte 170 | 171 | ``` 172 | 173 | ## No extra wrapper 174 | 175 | Actually, there is an HTML `
` wrapper, but it doesn't affect the layout because of [`display: contents`](https://caniuse.com/css-display-contents). If you use the `class` prop, the default display will be automatically removed. 176 | 177 | ## FAQ 178 | 179 | ### Why are we not using the `click` event to capture the `outclick` event? 180 | 181 | At first, we were using the `click` event to capture the `outclick` event, but later because of [this issue](https://github.com/babakfp/svelte-outclick/issues/4) we started using the `mousedown` event instead; and later because of [this issue](https://github.com/babakfp/svelte-outclick/issues/6) we started using the `pointerdown` even. Later I added the ability to use `pointerup` event with the `pointerdown` event to fully simulate the click event. 182 | 183 | ### Learn more 184 | 185 | - `keydown` event on `document.body` is ignored because this is how it works when you use `click` event instead. 186 | - `keydown` event only triggers with `Enter`, `NumpadEnter`, and `Space` keys (because this is how it works when you use the `click` event instead). 187 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-outclick", 3 | "version": "4.1.2", 4 | "description": "A Svelte component that allows you to listen to the clicks that happen outside of an element.", 5 | "license": "MIT", 6 | "repository": "github:babakfp/svelte-outclick", 7 | "scripts": { 8 | "dev": "vite dev", 9 | "build": "vite build && pnpm package", 10 | "preview": "vite preview", 11 | "prepare": "svelte-kit sync", 12 | "package": "svelte-kit sync && svelte-package -i src/package && publint", 13 | "prepublishOnly": "pnpm package", 14 | "format": "prettier -w .", 15 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 16 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" 17 | }, 18 | "peerDependencies": { 19 | "svelte": ">= 5" 20 | }, 21 | "devDependencies": { 22 | "@ianvs/prettier-plugin-sort-imports": "4.4.1", 23 | "@sveltejs/adapter-vercel": "5.7.2", 24 | "@sveltejs/kit": "2.21.1", 25 | "@sveltejs/package": "2.3.11", 26 | "@sveltejs/vite-plugin-svelte": "5.0.3", 27 | "@tailwindcss/vite": "4.1.7", 28 | "prettier": "3.5.3", 29 | "prettier-plugin-svelte": "3.4.0", 30 | "prettier-plugin-tailwindcss": "0.6.11", 31 | "publint": "0.3.12", 32 | "svelte": "5.32.1", 33 | "svelte-check": "4.2.1", 34 | "svelte-loading-bar": "2.0.4", 35 | "tailwindcss": "4.1.7", 36 | "tslib": "2.8.1", 37 | "typescript": "5.8.3", 38 | "vite": "6.3.5" 39 | }, 40 | "files": [ 41 | "dist" 42 | ], 43 | "exports": { 44 | ".": { 45 | "types": "./dist/index.d.ts", 46 | "svelte": "./dist/index.js" 47 | } 48 | }, 49 | "types": "./dist/index.d.ts", 50 | "svelte": "./dist/index.js", 51 | "keywords": [ 52 | "svelte", 53 | "outclick", 54 | "outside", 55 | "click" 56 | ], 57 | "type": "module" 58 | } 59 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import("prettier").Config & import("prettier-plugin-svelte").PluginConfig & import("@ianvs/prettier-plugin-sort-imports").PluginConfig} */ 2 | export default { 3 | semi: false, 4 | tabWidth: 4, 5 | plugins: [ 6 | "prettier-plugin-svelte", 7 | "prettier-plugin-tailwindcss", 8 | "@ianvs/prettier-plugin-sort-imports", 9 | ], 10 | importOrder: [ 11 | "^@", 12 | "", 13 | "^\\$(?!lib/)", 14 | "^\\$lib/", 15 | "^[.]", 16 | ], 17 | } 18 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @config "../tailwind.config.ts"; 3 | 4 | @layer base { 5 | * { 6 | @apply select-none; 7 | } 8 | svg.icon { 9 | @apply pointer-events-none inline-block h-[1em] w-[1em] fill-current; 10 | } 11 | } 12 | 13 | @layer components { 14 | .box { 15 | @apply relative z-10 flex h-20 items-center justify-center rounded border-2 border-dashed border-gray-700 bg-gray-900 text-center text-xs; 16 | } 17 | .box--blue { 18 | @apply bg-stripes-blue-400/20 border-blue-400/60; 19 | } 20 | .box--green { 21 | @apply bg-stripes-green-400/20 border-green-400/60; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Svelte OutClick 10 | 14 | 15 | 22 | 23 | 33 | 34 | %sveltekit.head% 35 | 36 | 37 |
%sveltekit.body%
38 | 39 | 40 | -------------------------------------------------------------------------------- /src/lib/Counter.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
9 | {#key count.value} 10 |
15 | {count.value} 16 |
17 | {/key} 18 |
19 | -------------------------------------------------------------------------------- /src/lib/IconMenu.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/lib/Menu.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | 27 |
28 | -------------------------------------------------------------------------------- /src/lib/count.svelte.ts: -------------------------------------------------------------------------------- 1 | export const count = $state({ value: 0 }) 2 | -------------------------------------------------------------------------------- /src/package/OutClick.svelte: -------------------------------------------------------------------------------- 1 | 124 | 125 | 126 | 131 | 132 | 139 | {@render children()} 140 | 141 | -------------------------------------------------------------------------------- /src/package/OutClickEvent.ts: -------------------------------------------------------------------------------- 1 | export type OutClickEvent = PointerEvent | KeyboardEvent 2 | -------------------------------------------------------------------------------- /src/package/castArray.ts: -------------------------------------------------------------------------------- 1 | export const castArray = (value: any) => { 2 | return Array.isArray(value) ? value : [value] 3 | } 4 | -------------------------------------------------------------------------------- /src/package/index.ts: -------------------------------------------------------------------------------- 1 | export { default as OutClick } from "./OutClick.svelte" 2 | export type { OutClickEvent } from "./OutClickEvent" 3 | -------------------------------------------------------------------------------- /src/package/types.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | HTMLAnchorAttributes, 3 | HTMLAreaAttributes, 4 | HTMLAttributes, 5 | HTMLAudioAttributes, 6 | HTMLBaseAttributes, 7 | HTMLBlockquoteAttributes, 8 | HTMLButtonAttributes, 9 | HTMLCanvasAttributes, 10 | HTMLColAttributes, 11 | HTMLColgroupAttributes, 12 | HTMLDataAttributes, 13 | HTMLDelAttributes, 14 | HTMLDetailsAttributes, 15 | HTMLDialogAttributes, 16 | HTMLEmbedAttributes, 17 | HTMLFieldsetAttributes, 18 | HTMLFormAttributes, 19 | HTMLHtmlAttributes, 20 | HTMLIframeAttributes, 21 | HTMLImgAttributes, 22 | HTMLInputAttributes, 23 | HTMLInsAttributes, 24 | HTMLKeygenAttributes, 25 | HTMLLabelAttributes, 26 | HTMLLiAttributes, 27 | HTMLLinkAttributes, 28 | HTMLMapAttributes, 29 | HTMLMenuAttributes, 30 | HTMLMetaAttributes, 31 | HTMLMeterAttributes, 32 | HTMLObjectAttributes, 33 | HTMLOlAttributes, 34 | HTMLOptgroupAttributes, 35 | HTMLOptionAttributes, 36 | HTMLOutputAttributes, 37 | HTMLParamAttributes, 38 | HTMLProgressAttributes, 39 | HTMLQuoteAttributes, 40 | HTMLScriptAttributes, 41 | HTMLSelectAttributes, 42 | HTMLSlotAttributes, 43 | HTMLSourceAttributes, 44 | HTMLStyleAttributes, 45 | HTMLTableAttributes, 46 | HTMLTdAttributes, 47 | HTMLTemplateAttributes, 48 | HTMLTextareaAttributes, 49 | HTMLThAttributes, 50 | HTMLTimeAttributes, 51 | HTMLTrackAttributes, 52 | HTMLVideoAttributes, 53 | HTMLWebViewAttributes, 54 | SVGAttributes, 55 | } from "svelte/elements" 56 | 57 | /** 58 | * A clean version of the `SvelteHTMLElements` type, from the `svelte/elements` module. 59 | * It doesn't include the Svelte special elements (`svelte:window` etc.), and only includes known valid HTML elements. 60 | */ 61 | export type HtmlElements = { 62 | a: HTMLAnchorAttributes 63 | abbr: HTMLAttributes 64 | address: HTMLAttributes 65 | area: HTMLAreaAttributes 66 | article: HTMLAttributes 67 | aside: HTMLAttributes 68 | audio: HTMLAudioAttributes 69 | b: HTMLAttributes 70 | base: HTMLBaseAttributes 71 | bdi: HTMLAttributes 72 | bdo: HTMLAttributes 73 | big: HTMLAttributes 74 | blockquote: HTMLBlockquoteAttributes 75 | body: HTMLAttributes 76 | br: HTMLAttributes 77 | button: HTMLButtonAttributes 78 | canvas: HTMLCanvasAttributes 79 | caption: HTMLAttributes 80 | cite: HTMLAttributes 81 | code: HTMLAttributes 82 | col: HTMLColAttributes 83 | colgroup: HTMLColgroupAttributes 84 | data: HTMLDataAttributes 85 | datalist: HTMLAttributes 86 | dd: HTMLAttributes 87 | del: HTMLDelAttributes 88 | details: HTMLDetailsAttributes 89 | dfn: HTMLAttributes 90 | dialog: HTMLDialogAttributes 91 | div: HTMLAttributes 92 | dl: HTMLAttributes 93 | dt: HTMLAttributes 94 | em: HTMLAttributes 95 | embed: HTMLEmbedAttributes 96 | fieldset: HTMLFieldsetAttributes 97 | figcaption: HTMLAttributes 98 | figure: HTMLAttributes 99 | footer: HTMLAttributes 100 | form: HTMLFormAttributes 101 | h1: HTMLAttributes 102 | h2: HTMLAttributes 103 | h3: HTMLAttributes 104 | h4: HTMLAttributes 105 | h5: HTMLAttributes 106 | h6: HTMLAttributes 107 | head: HTMLAttributes 108 | header: HTMLAttributes 109 | hgroup: HTMLAttributes 110 | hr: HTMLAttributes 111 | html: HTMLHtmlAttributes 112 | i: HTMLAttributes 113 | iframe: HTMLIframeAttributes 114 | img: HTMLImgAttributes 115 | input: HTMLInputAttributes 116 | ins: HTMLInsAttributes 117 | kbd: HTMLAttributes 118 | keygen: HTMLKeygenAttributes 119 | label: HTMLLabelAttributes 120 | legend: HTMLAttributes 121 | li: HTMLLiAttributes 122 | link: HTMLLinkAttributes 123 | main: HTMLAttributes 124 | map: HTMLMapAttributes 125 | mark: HTMLAttributes 126 | menu: HTMLMenuAttributes 127 | menuitem: HTMLAttributes 128 | meta: HTMLMetaAttributes 129 | meter: HTMLMeterAttributes 130 | nav: HTMLAttributes 131 | noscript: HTMLAttributes 132 | object: HTMLObjectAttributes 133 | ol: HTMLOlAttributes 134 | optgroup: HTMLOptgroupAttributes 135 | option: HTMLOptionAttributes 136 | output: HTMLOutputAttributes 137 | p: HTMLAttributes 138 | param: HTMLParamAttributes 139 | picture: HTMLAttributes 140 | pre: HTMLAttributes 141 | progress: HTMLProgressAttributes 142 | q: HTMLQuoteAttributes 143 | rp: HTMLAttributes 144 | rt: HTMLAttributes 145 | ruby: HTMLAttributes 146 | s: HTMLAttributes 147 | samp: HTMLAttributes 148 | slot: HTMLSlotAttributes 149 | script: HTMLScriptAttributes 150 | search: HTMLAttributes 151 | section: HTMLAttributes 152 | select: HTMLSelectAttributes 153 | small: HTMLAttributes 154 | source: HTMLSourceAttributes 155 | span: HTMLAttributes 156 | strong: HTMLAttributes 157 | style: HTMLStyleAttributes 158 | sub: HTMLAttributes 159 | summary: HTMLAttributes 160 | sup: HTMLAttributes 161 | table: HTMLTableAttributes 162 | template: HTMLTemplateAttributes 163 | tbody: HTMLAttributes 164 | td: HTMLTdAttributes 165 | textarea: HTMLTextareaAttributes 166 | tfoot: HTMLAttributes 167 | th: HTMLThAttributes 168 | thead: HTMLAttributes 169 | time: HTMLTimeAttributes 170 | title: HTMLAttributes 171 | tr: HTMLAttributes 172 | track: HTMLTrackAttributes 173 | u: HTMLAttributes 174 | ul: HTMLAttributes 175 | var: HTMLAttributes 176 | video: HTMLVideoAttributes 177 | wbr: HTMLAttributes 178 | webview: HTMLWebViewAttributes 179 | svg: SVGAttributes 180 | 181 | animate: SVGAttributes 182 | animateMotion: SVGAttributes 183 | animateTransform: SVGAttributes 184 | circle: SVGAttributes 185 | clipPath: SVGAttributes 186 | defs: SVGAttributes 187 | desc: SVGAttributes 188 | ellipse: SVGAttributes 189 | feBlend: SVGAttributes 190 | feColorMatrix: SVGAttributes 191 | feComponentTransfer: SVGAttributes 192 | feComposite: SVGAttributes 193 | feConvolveMatrix: SVGAttributes 194 | feDiffuseLighting: SVGAttributes 195 | feDisplacementMap: SVGAttributes 196 | feDistantLight: SVGAttributes 197 | feDropShadow: SVGAttributes 198 | feFlood: SVGAttributes 199 | feFuncA: SVGAttributes 200 | feFuncB: SVGAttributes 201 | feFuncG: SVGAttributes 202 | feFuncR: SVGAttributes 203 | feGaussianBlur: SVGAttributes 204 | feImage: SVGAttributes 205 | feMerge: SVGAttributes 206 | feMergeNode: SVGAttributes 207 | feMorphology: SVGAttributes 208 | feOffset: SVGAttributes 209 | fePointLight: SVGAttributes 210 | feSpecularLighting: SVGAttributes 211 | feSpotLight: SVGAttributes 212 | feTile: SVGAttributes 213 | feTurbulence: SVGAttributes 214 | filter: SVGAttributes 215 | foreignObject: SVGAttributes 216 | g: SVGAttributes 217 | image: SVGAttributes 218 | line: SVGAttributes 219 | linearGradient: SVGAttributes 220 | marker: SVGAttributes 221 | mask: SVGAttributes 222 | metadata: SVGAttributes 223 | mpath: SVGAttributes 224 | path: SVGAttributes 225 | pattern: SVGAttributes 226 | polygon: SVGAttributes 227 | polyline: SVGAttributes 228 | radialGradient: SVGAttributes 229 | rect: SVGAttributes 230 | stop: SVGAttributes 231 | switch: SVGAttributes 232 | symbol: SVGAttributes 233 | text: SVGAttributes 234 | textPath: SVGAttributes 235 | tspan: SVGAttributes 236 | use: SVGAttributes 237 | view: SVGAttributes 238 | } 239 | 240 | /** 241 | * Every known valid HTML tags. 242 | */ 243 | export type HtmlTags = keyof HtmlElements 244 | 245 | /** 246 | * Same as `HtmlTags`, but with `undefined`, for optional props. 247 | */ 248 | export type OptionalHtmlTags = HtmlTags | undefined 249 | 250 | /** 251 | * Filters out the `on` event attributes from element attributes. 252 | */ 253 | export type FilterNonEventAttributes = { 254 | [K in keyof T as K extends `on${string}` ? never : K]: T[K] 255 | } 256 | 257 | /** 258 | * Returns known valid attributes of a given HTML tag. 259 | * TTag is the HTML tag name, or `undefined` for the default tag. 260 | * TDefault is the default HTML tag name, if TTag is `undefined`. 261 | */ 262 | export type RestProps< 263 | TTag extends OptionalHtmlTags, 264 | TDefault extends HtmlTags, 265 | > = FilterNonEventAttributes< 266 | HtmlElements[TTag extends undefined ? TDefault : TTag] 267 | > 268 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 30 |
31 | 32 | 33 |
36 |
37 | 38 | TIMES CLICKED OUTSIDE 39 |
40 | 41 | {#key $page.url.pathname} 42 |
43 | {@render children()} 44 |
45 | 46 | {#if description} 47 |

51 | {@html description} 52 |

53 | {/if} 54 | {/key} 55 |
56 |
57 | -------------------------------------------------------------------------------- /src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | (count.value += 1)}> 7 | COMPONENT 8 | 9 | -------------------------------------------------------------------------------- /src/routes/exclude/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | (count.value += 1)} 9 | excludeQuerySelectorAll="#nothing-happens" 10 | > 11 | COMPONENT 12 | 13 | 14 |
15 | EXCLUDED (NOTHING HAPPENS) 16 |
17 | -------------------------------------------------------------------------------- /src/routes/half-click/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | (count.value += 1)} 9 | halfClick={true} 10 | > 11 | COMPONENT 12 | 13 | -------------------------------------------------------------------------------- /static/Exo2-VariableFont_wght.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babakfp/svelte-outclick/b407b2bc13198b9ace29ddb4ac3dbb85367f6d8a/static/Exo2-VariableFont_wght.woff2 -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babakfp/svelte-outclick/b407b2bc13198b9ace29ddb4ac3dbb85367f6d8a/static/favicon.png -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from "@sveltejs/adapter-vercel" 2 | import { vitePreprocess } from "@sveltejs/vite-plugin-svelte" 3 | 4 | /** @type {import("@sveltejs/kit").Config} */ 5 | export default { 6 | preprocess: vitePreprocess(), 7 | kit: { 8 | adapter: adapter(), 9 | alias: { 10 | "svelte-outclick": "src/package/index.ts", 11 | }, 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss" 2 | import colors from "tailwindcss/colors" 3 | import { default as flattenColorPalette } from "tailwindcss/lib/util/flattenColorPalette" 4 | 5 | export default { 6 | content: ["./src/**/*.{html,svelte}"], 7 | theme: { 8 | extend: { 9 | colors: { 10 | gray: colors.neutral, 11 | }, 12 | fontFamily: { 13 | sans: "Exo2", 14 | }, 15 | }, 16 | }, 17 | plugins: [ 18 | ({ theme, matchUtilities }) => { 19 | const backgroundSize = "4px 4px" 20 | const backgroundImage = (color: string) => 21 | `linear-gradient(135deg, ${color} 10%, transparent 10%, transparent 50%, ${color} 50%, ${color} 60%, transparent 60%, transparent 100%)` 22 | 23 | matchUtilities( 24 | { 25 | "bg-stripes": (value: string) => ({ 26 | "background-image": backgroundImage(value), 27 | "background-size": backgroundSize, 28 | }), 29 | }, 30 | { 31 | values: flattenColorPalette(theme("colors")), 32 | type: "color", 33 | }, 34 | ) 35 | }, 36 | ], 37 | } satisfies Config 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from "@sveltejs/kit/vite" 2 | import tailwindcss from "@tailwindcss/vite" 3 | import { defineConfig } from "vite" 4 | 5 | export default defineConfig({ 6 | plugins: [tailwindcss(), sveltekit()], 7 | }) 8 | --------------------------------------------------------------------------------