Button component improvements
buttons with a `href` now render as <a> elements, otherwise <button>
This commit is contained in:
		
							parent
							
								
									d0163ee094
								
							
						
					
					
						commit
						77665702b7
					
				
					 4 changed files with 64 additions and 72 deletions
				
			
		|  | @ -5,62 +5,62 @@ | |||
| 
 | ||||
|     const dispatch = createEventDispatcher(); | ||||
| 
 | ||||
|     let className = ""; | ||||
|     export { className as class }; | ||||
|     export let active = false; | ||||
|     export let filled = false; | ||||
|     export let disabled = false; | ||||
|     export let centered = false; | ||||
|     export let label = undefined; | ||||
|     export let sound = "default"; | ||||
|     export let href = false; | ||||
|     export let href = undefined; | ||||
|     export let onClick = undefined; | ||||
| 
 | ||||
|     let classes = []; | ||||
| 
 | ||||
|     function click() { | ||||
|         if (disabled) return; | ||||
|         if (href) { | ||||
|             const link = document.createElement('a'); | ||||
|             link.href = href; | ||||
|             link.dispatchEvent(new MouseEvent('click', { | ||||
|                 bubbles: true, | ||||
|                 cancelable: true, | ||||
|                 view: window, | ||||
|                 ctrlKey: event.ctrlKey, | ||||
|                 metaKey: event.metaKey, | ||||
|                 shiftKey: event.shiftKey, | ||||
|                 altKey: event.altKey, | ||||
|                 button: event.button, | ||||
|             })); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         playSound(sound); | ||||
|         dispatch('click'); | ||||
|     } | ||||
| 
 | ||||
|     afterUpdate(() => { | ||||
|         classes = []; | ||||
|         if (active) classes = ["active"]; | ||||
|         if (filled) classes = ["filled"]; | ||||
|         if (disabled) classes = ["disabled"]; | ||||
|         classes = className.split(' '); | ||||
|         if (active) classes.push("active"); | ||||
|         if (filled) classes.push("filled"); | ||||
|         if (disabled) classes.push("disabled"); | ||||
|         if (centered) classes.push("centered"); | ||||
|     }); | ||||
| </script> | ||||
| 
 | ||||
| <button | ||||
|         type="button" | ||||
|         class={classes.join(' ')} | ||||
|         title={label} | ||||
|         aria-label={label} | ||||
|         on:click={() => click()}> | ||||
|     <span class="icon"> | ||||
|         <slot name="icon" /> | ||||
|     </span> | ||||
|     <slot/> | ||||
| </button> | ||||
| {#if href} | ||||
|     <a | ||||
|             class={classes.join(' ')} | ||||
|             title={label} | ||||
|             aria-label={label} | ||||
|             href={href} | ||||
|             on:click={() => click()}> | ||||
|         <span class="icon"> | ||||
|             <slot name="icon" /> | ||||
|         </span> | ||||
|         <slot/> | ||||
|     </a> | ||||
| {:else} | ||||
|     <button | ||||
|             type="button" | ||||
|             class={classes.join(' ')} | ||||
|             title={label} | ||||
|             aria-label={label} | ||||
|             on:click={() => click()}> | ||||
|             <span class="icon"> | ||||
|                 <slot name="icon" /> | ||||
|             </span> | ||||
|             <slot/> | ||||
|     </button> | ||||
| {/if} | ||||
| 
 | ||||
| <style> | ||||
|     button { | ||||
|         width: 100%; | ||||
|     a, button { | ||||
|         height: fit-content; | ||||
|         padding: .7em .8em; | ||||
|         display: flex; | ||||
|  | @ -71,6 +71,7 @@ | |||
|         font-size: 1rem; | ||||
|         font-weight: 600; | ||||
|         text-align: left; | ||||
|         text-decoration: none; | ||||
| 
 | ||||
|         border-radius: 8px; | ||||
|         border: 2px solid var(--bg-700); | ||||
|  | @ -84,22 +85,32 @@ | |||
| 
 | ||||
|         cursor: pointer; | ||||
|     } | ||||
|     a { | ||||
|         width: calc(100% - 1.6em); | ||||
|     } | ||||
|     button { | ||||
|         width: 100%; | ||||
|     } | ||||
| 
 | ||||
|     a.centered, | ||||
|     button.centered { | ||||
|         text-align: center; | ||||
|         justify-content: center; | ||||
|     } | ||||
| 
 | ||||
|     a:hover, | ||||
|     button:hover { | ||||
|         border-color: color-mix(in srgb, var(--bg-700), black 10%); | ||||
|         background-color: color-mix(in srgb, var(--bg-700), black 10%); | ||||
|     } | ||||
| 
 | ||||
|     a:active, | ||||
|     button:active { | ||||
|         border-color: color-mix(in srgb, var(--bg-700), black 20%); | ||||
|         background-color: color-mix(in srgb, var(--bg-700), black 20%); | ||||
|     } | ||||
| 
 | ||||
|     a.active, | ||||
|     button.active { | ||||
|         background-color: var(--bg-600); | ||||
|         color: var(--accent); | ||||
|  | @ -107,34 +118,40 @@ | |||
|         text-shadow: 0px 2px 32px var(--accent); | ||||
|     } | ||||
| 
 | ||||
|     a.active:hover, | ||||
|     button.active:hover { | ||||
|         color: color-mix(in srgb, var(--accent), var(--bg-1000) 20%); | ||||
|         border-color: color-mix(in srgb, var(--accent), var(--bg-1000) 20%); | ||||
|         background-color: color-mix(in srgb, var(--bg-600), var(--accent) 10%); | ||||
|     } | ||||
| 
 | ||||
|     a.active:active, | ||||
|     button.active:active { | ||||
|         color: color-mix(in srgb, var(--accent), var(--bg-800) 10%); | ||||
|         border-color: color-mix(in srgb, var(--accent), var(--bg-800) 10%); | ||||
|         background-color: color-mix(in srgb, var(--bg-600), var(--bg-800) 10%); | ||||
|     } | ||||
| 
 | ||||
|     a.filled, | ||||
|     button.filled { | ||||
|         background-color: var(--accent); | ||||
|         color: var(--bg-800); | ||||
|         border-color: transparent; | ||||
|     } | ||||
| 
 | ||||
|     a.filled:hover, | ||||
|     button.filled:hover { | ||||
|         color: color-mix(in srgb, var(--bg-800), white 10%); | ||||
|         background-color: color-mix(in srgb, var(--accent), white 20%); | ||||
|     } | ||||
| 
 | ||||
|     a.filled:active, | ||||
|     button.filled:active { | ||||
|         color: color-mix(in srgb, var(--bg-800), black 10%); | ||||
|         background-color: color-mix(in srgb, var(--accent), black 20%); | ||||
|     } | ||||
| 
 | ||||
|     a.disabled, | ||||
|     button.disabled { | ||||
|         color: var(--text); | ||||
|         opacity: .5; | ||||
|  |  | |||
|  | @ -32,35 +32,6 @@ | |||
| 
 | ||||
|     const dispatch = createEventDispatcher(); | ||||
| 
 | ||||
|     function handle_btn(name) { | ||||
|         if (!$account) return; | ||||
|         let route; | ||||
|         switch (name) { | ||||
|             case "timeline": | ||||
|                 route = "/"; | ||||
|                 getTimeline(true); | ||||
|                 break; | ||||
|             case "notifications": | ||||
|                 route = "/notifications"; | ||||
|                 notifications.set([]); | ||||
|                 getNotifications(); | ||||
|                 break; | ||||
|             case "explore": | ||||
|             case "lists": | ||||
|             case "favourites": | ||||
|             case "bookmarks": | ||||
|             case "hashtags": | ||||
|             default: | ||||
|                 return; | ||||
|         } | ||||
|         if (!route) return; | ||||
|         window.scrollTo({ | ||||
|             top: 0, | ||||
|             behavior: "smooth" | ||||
|         }); | ||||
|         goto(route); | ||||
|     } | ||||
| 
 | ||||
|     function gotoProfile() { | ||||
|         if (!$account) return; | ||||
|         playSound(); | ||||
|  | @ -102,7 +73,7 @@ | |||
|     {#if $account} | ||||
|     <div id="nav-items"> | ||||
|         <Button label="Timeline" | ||||
|                 on:click={() => handle_btn("timeline")} | ||||
|                 href="/")} | ||||
|                 active={$page.url.pathname === "/"}> | ||||
|             <svelte:fragment slot="icon"> | ||||
|                 <TimelineIcon/> | ||||
|  | @ -110,7 +81,7 @@ | |||
|             {lang.string('navigation.timeline')} | ||||
|         </Button> | ||||
|         <Button label="Notifications" | ||||
|                 on:click={() => handle_btn("notifications")} | ||||
|                 href="notifications"} | ||||
|                 active={$page.url.pathname === "/notifications"}> | ||||
|             <svelte:fragment slot="icon"> | ||||
|                 <NotificationsIcon/> | ||||
|  | @ -183,7 +154,7 @@ | |||
|         <div id="account-button"> | ||||
|             <img src={$account.avatar_url} class="account-avatar" height="64px" alt="" aria-hidden="true" on:click={() => gotoProfile()}> | ||||
|             <div class="account-name" aria-hidden="true"> | ||||
|                 <a href="/{$server.host}/{$account.username}" class="nickname" title={$account.nickname}>{@html $account.rich_name}</a> | ||||
|                 <a href="/{$server.host}/@{$account.username}" class="nickname" title={$account.nickname}>{@html $account.rich_name}</a> | ||||
|                 <span class="username" title={`@${$account.username}@${$account.host}`}> | ||||
|                     {$account.fqn} | ||||
|                 </span> | ||||
|  |  | |||
|  | @ -12,12 +12,12 @@ | |||
| </script> | ||||
| 
 | ||||
| <div class={"post-header-container" + (reply ? " reply" : "")}> | ||||
|     <a href="/{$server.host}/{post.account.fqn}" class="post-avatar-container" on:mouseup|stopPropagation> | ||||
|     <a href="/{$server.host}/@{post.account.fqn}" class="post-avatar-container" on:mouseup|stopPropagation> | ||||
|         <img src={post.account.avatar_url} type={post.account.avatar_type} alt="" width="48" height="48" class="post-avatar" loading="lazy" decoding="async"> | ||||
|     </a> | ||||
|     <header class="post-header"> | ||||
|         <div class="post-user-info" on:mouseup|stopPropagation> | ||||
|             <a href="/{$server.host}/{post.account.fqn}" class="name">{@html post.account.rich_name}</a> | ||||
|             <a href="/{$server.host}/@{post.account.fqn}" class="name">{@html post.account.rich_name}</a> | ||||
|             <span class="username">{post.account.mention}</span> | ||||
|         </div> | ||||
|         <div class="post-info" on:mouseup|stopPropagation> | ||||
|  |  | |||
|  | @ -78,7 +78,7 @@ | |||
|         </ul> | ||||
|         <div class="profile-actions"> | ||||
|             {#if $account && profile.fqn !== $account.fqn} | ||||
|             <Button disabled filled label="{lang.string('profile.follow')} {profile.nickname}" class="profile-btn-follow"> | ||||
|             <Button filled label="{lang.string('profile.follow')} {profile.nickname}" class="profile-btn-follow"> | ||||
|                 {lang.string('profile.follow')} | ||||
|             </Button> | ||||
|             {/if} | ||||
|  | @ -186,13 +186,17 @@ | |||
|         gap: .5rem; | ||||
|     } | ||||
| 
 | ||||
|     .profile-btn-follow { | ||||
|     .profile-actions :global(button.profile-btn-follow) { | ||||
|         padding: 0 32px; | ||||
|     } | ||||
| 
 | ||||
|     .profile-actions :global(button) { | ||||
|         height: 42px; | ||||
|     .profile-actions :global(a) { | ||||
|         width: fit-content; | ||||
|         height: 16px; | ||||
|     } | ||||
|     .profile-actions :global(button) { | ||||
|         width: fit-content; | ||||
|         height: 42px; | ||||
|     } | ||||
| 
 | ||||
|     .profile-post-categories { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue