Skip to content

Commit 1ed0d82

Browse files
committed
feat: Added use-favicon.
1 parent e415947 commit 1ed0d82

File tree

8 files changed

+106
-21
lines changed

8 files changed

+106
-21
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Based on the [@mantine/hooks](https://github.com/mantinedev/mantine/tree/master/
6262
- [ ] use-document-visibility
6363
- [ ] use-event-listener
6464
- [ ] use-eye-dropper
65-
- [ ] use-favicon
65+
- [x] use-favicon
6666
- [ ] use-fetch
6767
- [ ] use-focus-return
6868
- [ ] use-focus-trap

dev/components/examples/example-base.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export function ExampleBase(props: FlowProps<ExampleBaseProps>) {
1212
const [viewing, setViewing] = createSignal<'code' | 'result'>('result');
1313

1414
return (
15-
<div class="flex h-full max-h-[500px] w-full flex-col items-start gap-y-5 overflow-hidden rounded-xl border bg-white p-5">
15+
<div class="flex h-full max-h-[500px] w-full flex-col items-start gap-y-3 overflow-hidden rounded-xl border bg-white p-5">
1616
<div class="flex w-full items-center justify-between">
17-
<h2 class="text-2xl font-bold">{props.title}</h2>
17+
<h2 class="text-xl font-bold">{props.title}</h2>
1818
<button
1919
class={`rounded-md border p-2 transition active:scale-95 ${viewing() === 'code' ? 'bg-background text-white' : ''}`}
2020
onClick={() => {
@@ -24,15 +24,16 @@ export function ExampleBase(props: FlowProps<ExampleBaseProps>) {
2424
<IconCode class="h-5 w-5" />
2525
</button>
2626
</div>
27-
<p>{props.description}</p>
27+
28+
<p class="text-sm">{props.description}</p>
2829

2930
<Show when={viewing() === 'result'}>
3031
<div class="w-full flex-1 rounded-md">{props.children}</div>
3132
</Show>
3233

3334
<div
3435
style={{ display: viewing() === 'code' ? 'block' : 'none' }}
35-
class="w-full flex-1 overflow-hidden rounded-md border bg-[#1c1e28] p-3 text-white"
36+
class="w-full flex-1 overflow-auto rounded-md border bg-[#1c1e28] p-3 text-white"
3637
>
3738
{props.code}
3839
</div>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
```tsx
2+
const [favicon, setFavicon] = createSignal('https://docs.solidjs.com/favicon.svg');
3+
const setXFavicon = () => setFavicon('https://x.com/favicon.ico');
4+
const setSolidFavicon = () => setFavicon('https://docs.solidjs.com/favicon.svg');
5+
6+
useFavicon(favicon);
7+
8+
return (
9+
<button onClick={setXFavicon}>Use X Favicon</button>
10+
<button onClick={setSolidFavicon}>Use SolidJS Favicon</button>
11+
)
12+
```
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { useFavicon } from 'src';
2+
import { ExampleBase } from '../example-base';
3+
import Code from './use-favicon.code.mdx';
4+
5+
import { createSignal } from 'solid-js';
6+
import { useMDXComponents } from 'solid-jsx';
7+
8+
export function UseFaviconExample() {
9+
const [favicon, setFavicon] = createSignal('https://docs.solidjs.com/favicon.svg');
10+
const setXFavicon = () => setFavicon('https://x.com/favicon.ico');
11+
const setSolidFavicon = () => setFavicon('https://docs.solidjs.com/favicon.svg');
12+
13+
useFavicon(favicon);
14+
15+
// @ts-ignore
16+
const components: any = useMDXComponents();
17+
18+
return (
19+
<ExampleBase
20+
title="useFavicon"
21+
description="Appends <link /> element to head component with given favicon url. The hook is not called during server side rendering."
22+
code={<Code components={components} />}
23+
>
24+
<div class="flex h-full w-full flex-col items-center justify-center gap-3 rounded-md border p-3 py-10 text-center text-sm">
25+
<button
26+
onClick={() => setXFavicon()}
27+
class="rounded-md border px-2 py-1.5 transition active:scale-95"
28+
>
29+
X favicon
30+
</button>
31+
32+
<button
33+
onClick={() => setSolidFavicon()}
34+
class="rounded-md border px-2 py-1.5 transition active:scale-95"
35+
>
36+
Solid favicon
37+
</button>
38+
</div>
39+
</ExampleBase>
40+
);
41+
}

dev/components/markdown/markdown.context.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ export function MarkdownContextProvider(props: FlowProps) {
5454
fallback={<IconCopy width={23} height={23} />}
5555
/>
5656
</button>
57-
<div ref={setRef}>
58-
{/* This will be replaced, but render it at first paint with opacity 0 so it takes up space. */}
57+
<div ref={setRef} class="text-sm">
58+
{/* This will be replaced with the code element, but render it at first paint with opacity 0 so it takes up space. */}
5959
<div class="opacity-0">{props.children}</div>
6060
</div>
6161
</div>

dev/pages/index/+Page.tsx

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// import { createMemo, type Component } from 'solid-js';
22

33
// Hooks
4-
import { ExampleBase } from 'dev/components/examples/example-base';
54
import { UseClickOutsideExample } from 'dev/components/examples/use-click-outside/use-click-outside.example';
65
import { UseElementSizeExample } from 'dev/components/examples/use-element-size/use-element-size.example';
6+
import { UseFaviconExample } from 'dev/components/examples/use-favicon/use-favicon.example';
77
import { UseHotkeysExample } from 'dev/components/examples/use-hotkeys/use-hotkeys.example';
88
import { UseHoverExample } from 'dev/components/examples/use-hover/use-hover.example';
99
import { UseIdleExample } from 'dev/components/examples/use-idle/use-idle.example';
@@ -12,21 +12,9 @@ import { UseNetworkExample } from 'dev/components/examples/use-network/use-netwo
1212
import { UseOsExample } from 'dev/components/examples/use-os/use-os.example';
1313
import { UseResizeObserverExample } from 'dev/components/examples/use-resize-observer/use-resize-observer.example';
1414
import { UseToggleExample } from 'dev/components/examples/use-toggle/use-toggle.example';
15-
import { MarkdownContextProvider } from 'dev/components/markdown/markdown.context';
1615
import { IconLogo } from 'dev/icons';
17-
import { title } from 'process';
1816
import { createMemo, createSignal, For } from 'solid-js';
19-
import {
20-
useClickOutside,
21-
useElementSize,
22-
useHotkeys,
23-
useHover,
24-
useIdle,
25-
useNetwork,
26-
useOs,
27-
useResizeObserver,
28-
useToggle,
29-
} from 'src';
17+
import { useOs } from 'src';
3018

3119
export default function HomePage() {
3220
// // let ref = useClickOutside(() =>
@@ -91,6 +79,10 @@ export default function HomePage() {
9179
title: 'useToggle',
9280
example: <UseToggleExample />,
9381
},
82+
{
83+
title: 'useFavicon',
84+
example: <UseFaviconExample />,
85+
},
9486
];
9587

9688
const filteredList = createMemo(() => {

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './use-click-outside/use-click-outside';
2+
export * from './use-favicon/use-favicon';
23
export * from './use-hotkeys/use-hotkeys';
34
export * from './use-hover/use-hover';
45
export * from './use-idle/use-idle';

src/use-favicon/use-favicon.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { Accessor, createEffect, createSignal } from 'solid-js';
2+
3+
const MIME_TYPES: Record<string, string> = {
4+
ico: 'image/x-icon',
5+
png: 'image/png',
6+
svg: 'image/svg+xml',
7+
gif: 'image/gif',
8+
};
9+
10+
/**
11+
* A hook that sets the favicon of the page based on the url (accessor) passed.
12+
*/
13+
export function useFavicon(url: Accessor<string>) {
14+
const [linkRef, setLinkRef] = createSignal<HTMLLinkElement>();
15+
16+
createEffect(() => {
17+
if (!url()) {
18+
return;
19+
}
20+
21+
if (!linkRef()) {
22+
const existingElements = document.querySelectorAll<HTMLLinkElement>('link[rel*="icon"]');
23+
existingElements.forEach(element => document.head.removeChild(element));
24+
25+
const element = document.createElement('link');
26+
element.rel = 'shortcut icon';
27+
setLinkRef(element);
28+
document.querySelector('head')!.appendChild(element);
29+
}
30+
31+
const splittedUrl = url().split('.');
32+
linkRef()?.setAttribute(
33+
'type',
34+
MIME_TYPES[splittedUrl?.[splittedUrl.length - 1]?.toLowerCase()!]!,
35+
);
36+
linkRef()?.setAttribute('href', url());
37+
});
38+
}

0 commit comments

Comments
 (0)