Tabs: fix misplaced indicator
This commit is contained in:
parent
66b593add8
commit
0eef74f25f
4 changed files with 58 additions and 67 deletions
23
src/material/Tab.css
Normal file
23
src/material/Tab.css
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
.Tab {
|
||||||
|
cursor: pointer;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
height: 100%;
|
||||||
|
max-width: min(calc(100% - 56px), 264px);
|
||||||
|
padding: 10px 24px;
|
||||||
|
font-size: 0.8135rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: color 120ms var(--tutu-anim-curve-std);
|
||||||
|
}
|
||||||
|
|
||||||
|
.MuiToolbar-root .Tab {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&.focus,
|
||||||
|
&.Tabs-focus {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,26 +1,18 @@
|
||||||
import {
|
import {
|
||||||
Component,
|
|
||||||
createEffect,
|
createEffect,
|
||||||
splitProps,
|
splitProps,
|
||||||
type JSX,
|
type JSX,
|
||||||
type ParentComponent,
|
type ParentComponent,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import { css } from "solid-styled";
|
|
||||||
import { useTabListContext } from "./Tabs";
|
import { useTabListContext } from "./Tabs";
|
||||||
|
import "./Tab.css";
|
||||||
|
|
||||||
const Tab: ParentComponent<
|
const Tab: ParentComponent<
|
||||||
{
|
{
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
large?: boolean;
|
|
||||||
} & JSX.ButtonHTMLAttributes<HTMLButtonElement>
|
} & JSX.ButtonHTMLAttributes<HTMLButtonElement>
|
||||||
> = (props) => {
|
> = (props) => {
|
||||||
const [managed, rest] = splitProps(props, [
|
const [managed, rest] = splitProps(props, ["focus", "type", "role", "ref"]);
|
||||||
"focus",
|
|
||||||
"large",
|
|
||||||
"type",
|
|
||||||
"role",
|
|
||||||
"ref",
|
|
||||||
]);
|
|
||||||
let self: HTMLButtonElement;
|
let self: HTMLButtonElement;
|
||||||
const {
|
const {
|
||||||
focusOn: [, setFocusOn],
|
focusOn: [, setFocusOn],
|
||||||
|
@ -35,32 +27,7 @@ const Tab: ParentComponent<
|
||||||
}
|
}
|
||||||
return managed.focus;
|
return managed.focus;
|
||||||
});
|
});
|
||||||
css`
|
|
||||||
.tab {
|
|
||||||
cursor: pointer;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
min-width: ${managed.large ? "160px" : "72px"};
|
|
||||||
height: 48px;
|
|
||||||
max-width: min(calc(100% - 56px), 264px);
|
|
||||||
padding: 10px 24px;
|
|
||||||
font-size: 0.8135rem;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
transition: color 120ms var(--tutu-anim-curve-std);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.MuiToolbar-root) .tab {
|
|
||||||
color: rgba(255, 255, 255, 0.7);
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus,
|
|
||||||
&.focus,
|
|
||||||
&:global(.tablist-focus) {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
ref={(x) => {
|
ref={(x) => {
|
||||||
|
@ -68,7 +35,7 @@ const Tab: ParentComponent<
|
||||||
(managed.ref as (e: HTMLButtonElement) => void)?.(x);
|
(managed.ref as (e: HTMLButtonElement) => void)?.(x);
|
||||||
}}
|
}}
|
||||||
type={managed.type ?? "button"}
|
type={managed.type ?? "button"}
|
||||||
classList={{ tab: true, focus: managed.focus }}
|
classList={{ Tab: true, focus: managed.focus }}
|
||||||
role={managed.role ?? "tab"}
|
role={managed.role ?? "tab"}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
|
|
21
src/material/Tabs.css
Normal file
21
src/material/Tabs.css
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
.Tabs {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: auto;
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
transition:
|
||||||
|
left var(--tabs-indkt-movspeed-offset, 0) var(--tutu-anim-curve-std),
|
||||||
|
width var(--tabs-indkt-movspeed-width, 0) var(--tutu-anim-curve-std);
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
background-color: white;
|
||||||
|
height: 2px;
|
||||||
|
width: var(--tabs-indkt-width, 0);
|
||||||
|
left: var(--tabs-indkt-offset, 0);
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
import {
|
import {
|
||||||
ParentComponent,
|
ParentComponent,
|
||||||
createContext,
|
createContext,
|
||||||
createEffect,
|
|
||||||
createMemo,
|
|
||||||
createRenderEffect,
|
createRenderEffect,
|
||||||
createSignal,
|
createSignal,
|
||||||
useContext,
|
useContext,
|
||||||
type Signal,
|
type Signal,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import { css } from "solid-styled";
|
import "./Tabs.css"
|
||||||
|
|
||||||
const TabListContext = /* @__PURE__ */ createContext<{
|
const TabListContext = /* @__PURE__ */ createContext<{
|
||||||
focusOn: Signal<HTMLElement[]>;
|
focusOn: Signal<HTMLElement[]>;
|
||||||
|
@ -24,7 +22,7 @@ export function useTabListContext() {
|
||||||
|
|
||||||
const ANIM_SPEED = 160 / 110; // 160px/110ms
|
const ANIM_SPEED = 160 / 110; // 160px/110ms
|
||||||
|
|
||||||
const TABLIST_FOCUS_CLASS = "tablist-focus";
|
const TABS_FOCUS_CLASS = "Tabs-focus";
|
||||||
|
|
||||||
const Tabs: ParentComponent<{
|
const Tabs: ParentComponent<{
|
||||||
offset?: number;
|
offset?: number;
|
||||||
|
@ -37,11 +35,11 @@ const Tabs: ParentComponent<{
|
||||||
const current = focusOn();
|
const current = focusOn();
|
||||||
if (lastFocusElement) {
|
if (lastFocusElement) {
|
||||||
for (const e of lastFocusElement) {
|
for (const e of lastFocusElement) {
|
||||||
e.classList.remove(TABLIST_FOCUS_CLASS);
|
e.classList.remove(TABS_FOCUS_CLASS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const e of current) {
|
for (const e of current) {
|
||||||
e.classList.add("tablist-focus");
|
e.classList.add(TABS_FOCUS_CLASS);
|
||||||
}
|
}
|
||||||
return current;
|
return current;
|
||||||
});
|
});
|
||||||
|
@ -109,7 +107,7 @@ const Tabs: ParentComponent<{
|
||||||
return ["0px", "0px", "110ms", "110ms"] as const;
|
return ["0px", "0px", "110ms", "110ms"] as const;
|
||||||
}
|
}
|
||||||
const rect = focusBoundingClientRect();
|
const rect = focusBoundingClientRect();
|
||||||
const rootRect = self.getBoundingClientRect();
|
const rootRect = self!.getBoundingClientRect();
|
||||||
const left = rect.x - rootRect.x;
|
const left = rect.x - rootRect.x;
|
||||||
const width = rect.width;
|
const width = rect.width;
|
||||||
const [prevEl, nextEl] = focusSiblings();
|
const [prevEl, nextEl] = focusSiblings();
|
||||||
|
@ -130,32 +128,14 @@ const Tabs: ParentComponent<{
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
css`
|
|
||||||
.tablist {
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow-x: auto;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
transition:
|
|
||||||
left ${indicator()[2]} var(--tutu-anim-curve-std),
|
|
||||||
width ${indicator()[3]} var(--tutu-anim-curve-std);
|
|
||||||
position: absolute;
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
background-color: white;
|
|
||||||
height: 2px;
|
|
||||||
width: ${indicator()[1]};
|
|
||||||
left: ${indicator()[0]};
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TabListContext.Provider value={{ focusOn: [focusOn, setFocusOn] }}>
|
<TabListContext.Provider value={{ focusOn: [focusOn, setFocusOn] }}>
|
||||||
<div ref={self!} class="tablist" role="tablist">
|
<div ref={self!} class="Tabs" style={{
|
||||||
|
"--tabs-indkt-width": indicator()[1],
|
||||||
|
"--tabs-indkt-offset": indicator()[0],
|
||||||
|
"--tabs-indkt-movspeed-offset": indicator()[2],
|
||||||
|
"--tabs-indkt-movspeed-width": indicator()[3]
|
||||||
|
}} role="tablist">
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
</TabListContext.Provider>
|
</TabListContext.Provider>
|
||||||
|
|
Loading…
Add table
Reference in a new issue