[READ-ONLY] Mirror of https://github.com/flo-bit/ui-kit. 🦊 fox ui, svelte 5 and tailwind 4 flo-bit.dev/ui-kit/
svelte tailwindcss ui-components
0

Configure Feed

Select the types of activity you want to include in your feed.

improve richtexteditor, blueskyloginmodal, tooltip

Florian (Jul 9, 2025, 3:13 AM +0200) 62d40f18 2ded5da1

+95 -34
+1 -1
README.md
··· 74 74 git clone https://github.com/flo-bit/ui-kit.git 75 75 cd ui-kit 76 76 pnpm install 77 - pnpm run dev 77 + pnpm run dev:docs 78 78 ``` 79 79 80 80 This ui kit is organized as a monorepo, with `apps/docs` being the documentation and the ui-kit library being split into multiple packages in `packages/`.
+7
.changeset/polite-regions-tan.md
··· 1 + --- 2 + '@fuxui/social': patch 3 + '@fuxui/base': patch 4 + '@fuxui/text': patch 5 + --- 6 + 7 + improve richtexteditor, blueskyloginmodal, tooltip
+5 -5
apps/docs/src/routes/+page.svelte
··· 24 24 > 25 25 Build Beautiful Apps & Websites Fast 26 26 </h1> 27 - 28 - <Badge class="fixed bottom-1 left-1"> 29 - {#if dev} 27 + 28 + {#if dev} 29 + <Badge class="fixed bottom-1 left-1"> 30 30 {count} components 31 - {/if} 32 - </Badge> 31 + </Badge> 32 + {/if} 33 33 34 34 <div class="mb-16 flex gap-2"> 35 35 <Button href={base + '/docs/quick-start'} class="mb-8">Quick Start</Button>
+2 -2
apps/docs/src/lib/site-components/Cards.svelte
··· 12 12 <div class="grid w-full grid-cols-1 gap-12 sm:grid-cols-2 lg:grid-cols-3"> 13 13 {#each category.components as card} 14 14 <div 15 - class="group relative flex flex-col items-start gap-3 transition-opacity duration-150 hover:opacity-90 md:gap-4" 15 + class="group relative flex flex-col items-start gap-3 transition-opacity duration-150 md:gap-4" 16 16 > 17 17 <div 18 - class="bg-base-50 dark:bg-base-900/30 border-base-200 dark:border-base-900 pointer-events-none relative h-44 w-full overflow-hidden rounded-2xl border" 18 + class="bg-base-50 dark:bg-base-900/50 shadow-lg backdrop-blur-xl shadow-base-900/10 inset-shadow-sm inset-shadow-base-800/5 dark:inset-shadow-base-400/5 border-base-200 dark:border-base-900 pointer-events-none relative h-44 w-full overflow-hidden rounded-2xl border" 19 19 > 20 20 <div 21 21 class={cn(
+5 -1
packages/base/src/lib/components/tooltip/tooltip.svelte
··· 38 38 </Tooltip.Trigger> 39 39 40 40 <Tooltip.Portal> 41 - <TooltipContent sideOffset={contentProps.sideOffset ?? 6} side={contentProps.side ?? 'top'}> 41 + <TooltipContent 42 + sideOffset={contentProps.sideOffset ?? 6} 43 + side={contentProps.side ?? 'top'} 44 + class={contentProps.class} 45 + > 42 46 {#if content} 43 47 {@render content()} 44 48 {:else}
+46 -14
packages/social/src/lib/components/bluesky-login/BlueskyLoginModal.svelte
··· 7 7 </script> 8 8 9 9 <script lang="ts"> 10 - import { Button, Modal, Subheading, Label, Input } from '@fuxui/base'; 10 + import { Button, Modal, Subheading, Label, Input, Avatar } from '@fuxui/base'; 11 11 import type { BlueskyLoginProps } from '.'; 12 12 13 13 let value = $state(''); 14 14 let error: string | null = $state(null); 15 15 let loading = $state(false); 16 16 17 - let { 18 - login, 19 - formAction, 20 - formMethod = 'get' 21 - }: BlueskyLoginProps = $props(); 17 + let { login, formAction, formMethod = 'get' }: BlueskyLoginProps = $props(); 22 18 23 - async function onSubmit(evt: Event) { 24 - if (formAction || !login) return; 19 + async function onLogin(handle: string) { 20 + if (loading || !login) return; 25 21 26 - evt.preventDefault(); 27 - if (loading) return; 28 - 29 - error = null; 30 22 loading = true; 23 + error = null; 31 24 32 25 try { 33 - const hide = await login(value); 26 + const hide = await login(handle); 34 27 35 28 if (hide) { 36 29 blueskyLoginModalState.hide(); ··· 43 36 } 44 37 } 45 38 39 + async function onSubmit(evt: Event) { 40 + if (formAction || !login) return; 41 + evt.preventDefault(); 42 + 43 + await onLogin(value); 44 + } 45 + 46 46 let input: HTMLInputElement | null = $state(null); 47 + 48 + let lastLogin: { handle: string; avatar: string } | null = $state(null); 49 + 50 + $effect(() => { 51 + let lastLoginDid = localStorage.getItem('last-login'); 52 + 53 + if (lastLoginDid) { 54 + let profile = localStorage.getItem(`profile-${lastLoginDid}`); 55 + 56 + if (profile) { 57 + lastLogin = JSON.parse(profile) 58 + } 59 + } 60 + }); 47 61 </script> 48 62 49 63 <Modal ··· 83 97 </a>, then come back here. 84 98 </div> 85 99 100 + {#if lastLogin} 101 + <Label for="bluesky-handle" class="mt-4 text-sm">Recent login:</Label> 102 + <Button 103 + class="max-w-xs overflow-x-hidden justify-start truncate" 104 + variant="primary" 105 + onclick={() => onLogin(lastLogin?.handle ?? '')} 106 + disabled={loading} 107 + > 108 + <Avatar src={lastLogin.avatar} class="size-6" /> 109 + 110 + <div 111 + class="text-accent-600 dark:text-accent-400 text-md max-w-full truncate overflow-x-hidden font-semibold" 112 + > 113 + <p>{loading ? 'Loading...' : lastLogin.handle}</p> 114 + </div> 115 + </Button> 116 + {/if} 117 + 86 118 <div class="mt-4 w-full"> 87 119 <Label for="bluesky-handle" class="text-sm">Your handle</Label> 88 120 <div class="mt-2"> ··· 102 134 <p class="text-accent-500 mt-2 text-sm font-medium">{error}</p> 103 135 {/if} 104 136 105 - <Button type="submit" class="ml-auto mt-2 w-full md:w-auto" disabled={loading} 137 + <Button type="submit" class="mt-2 ml-auto w-full md:w-auto" disabled={loading} 106 138 >{loading ? 'Loading...' : 'Login'}</Button 107 139 > 108 140 </form>
+11 -10
packages/text/src/lib/components/rich-text-editor/RichTextEditor.svelte
··· 18 18 import './code.css'; 19 19 import { cn } from '@fuxui/base'; 20 20 import { ImageUploadNode } from './image-upload/ImageUploadNode'; 21 + import { Transaction } from '@tiptap/pm/state'; 21 22 22 23 let { 23 24 content = $bindable({}), ··· 33 34 editor?: Editor | null; 34 35 ref?: HTMLDivElement | null; 35 36 class?: string; 36 - onupdate?: (content: Content) => void; 37 + onupdate?: (content: Content, context: { editor: Editor; transaction: Transaction }) => void; 37 38 ontransaction?: () => void; 38 39 } = $props(); 39 40 ··· 159 160 upload: async (file, onProgress, abortSignal) => { 160 161 console.log('uploading image', file); 161 162 // wait 2 seconds 162 - for(let i = 0; i < 10; i++) { 163 + for (let i = 0; i < 10; i++) { 163 164 await new Promise((resolve) => setTimeout(resolve, 200)); 164 165 onProgress?.({ progress: i / 10 }); 165 166 } ··· 179 180 }, 180 181 onUpdate: (ctx) => { 181 182 content = ctx.editor.getJSON(); 182 - onupdate?.(content); 183 - console.log('content', content); 183 + onupdate?.(content, ctx); 184 184 }, 185 185 onFocus: () => { 186 186 hasFocus = true; ··· 215 215 } 216 216 ontransaction?.(); 217 217 }, 218 - content: `` 218 + content 219 219 }); 220 220 221 221 menu?.classList.remove('hidden'); 222 222 menuLink?.classList.remove('hidden'); 223 - 224 223 }); 225 224 226 225 // Flag to track whether a file is being dragged over the drop area ··· 246 245 localImageUrls.add(localUrl); 247 246 248 247 //editor.commands.setImageUploadNode(); 249 - editor.chain().focus().setImageUploadNode( 250 - { 248 + editor 249 + .chain() 250 + .focus() 251 + .setImageUploadNode({ 251 252 preview: localUrl 252 - } 253 - ).run(); 253 + }) 254 + .run(); 254 255 255 256 // wait 2 seconds 256 257 // await new Promise((resolve) => setTimeout(resolve, 500));
+18 -1
apps/docs/src/routes/(main)/components/base/button/Example.svelte
··· 4 4 5 5 <h3>Primary</h3> 6 6 7 - <div class="flex items-center gap-2"> 7 + <div class="flex items-center gap-2 flex-wrap"> 8 8 <Button size="sm" 9 9 ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"> 10 10 <path ··· 46 46 /> 47 47 </svg>large</Button 48 48 > 49 + 50 + 51 + <Button disabled 52 + ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"> 53 + <path 54 + d="M11.7 2.805a.75.75 0 0 1 .6 0A60.65 60.65 0 0 1 22.83 8.72a.75.75 0 0 1-.231 1.337 49.948 49.948 0 0 0-9.902 3.912l-.003.002c-.114.06-.227.119-.34.18a.75.75 0 0 1-.707 0A50.88 50.88 0 0 0 7.5 12.173v-.224c0-.131.067-.248.172-.311a54.615 54.615 0 0 1 4.653-2.52.75.75 0 0 0-.65-1.352 56.123 56.123 0 0 0-4.78 2.589 1.858 1.858 0 0 0-.859 1.228 49.803 49.803 0 0 0-4.634-1.527.75.75 0 0 1-.231-1.337A60.653 60.653 0 0 1 11.7 2.805Z" 55 + /> 56 + <path 57 + d="M13.06 15.473a48.45 48.45 0 0 1 7.666-3.282c.134 1.414.22 2.843.255 4.284a.75.75 0 0 1-.46.711 47.87 47.87 0 0 0-8.105 4.342.75.75 0 0 1-.832 0 47.87 47.87 0 0 0-8.104-4.342.75.75 0 0 1-.461-.71c.035-1.442.121-2.87.255-4.286.921.304 1.83.634 2.726.99v1.27a1.5 1.5 0 0 0-.14 2.508c-.09.38-.222.753-.397 1.11.452.213.901.434 1.346.66a6.727 6.727 0 0 0 .551-1.607 1.5 1.5 0 0 0 .14-2.67v-.645a48.549 48.549 0 0 1 3.44 1.667 2.25 2.25 0 0 0 2.12 0Z" 58 + /> 59 + <path 60 + d="M4.462 19.462c.42-.419.753-.89 1-1.395.453.214.902.435 1.347.662a6.742 6.742 0 0 1-1.286 1.794.75.75 0 0 1-1.06-1.06Z" 61 + /> 62 + </svg>disabled</Button 63 + > 64 + 49 65 </div> 66 + 50 67 51 68 <h3>Secondary</h3> 52 69