Skip to content

Commit cc43857

Browse files
committed
[chrome] refactor TabStrip
1 parent 3f9b398 commit cc43857

File tree

5 files changed

+312
-295
lines changed

5 files changed

+312
-295
lines changed

packages/chrome/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ComponentContext } from "dreamland/core";
2-
import { Tabs } from "./components/TabStrip";
2+
import { TabStrip } from "./components/TabStrip/TabStrip";
33
import { browser } from "./Browser";
44
import type { Tab } from "./Tab";
55
import { BookmarksStrip } from "./components/BookmarksStrip";
@@ -34,7 +34,7 @@ export function App(_, cx: ComponentContext) {
3434

3535
return (
3636
<div id="app">
37-
<Tabs
37+
<TabStrip
3838
tabs={use(browser.tabs)}
3939
activetab={use(browser.activetab)}
4040
addTab={() => {
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import { css, type ComponentContext } from "dreamland/core";
2+
import type { Tab } from "../../Tab";
3+
import { setContextMenu } from "../Menu";
4+
import { iconClose, iconDuplicate, iconNew, iconRefresh } from "../../icons";
5+
import { browser, forceScreenshot } from "../../Browser";
6+
import { Icon } from "../Icon";
7+
import { TabTooltip } from "./TabTooltip";
8+
9+
export function DragTab(
10+
this: { tooltipActive: boolean },
11+
props: {
12+
active: boolean;
13+
id: number;
14+
tab: Tab;
15+
mousedown: (e: MouseEvent) => void;
16+
destroy: () => void;
17+
transitionend: () => void;
18+
},
19+
cx: ComponentContext
20+
) {
21+
this.tooltipActive = false;
22+
cx.mount = () => {
23+
setContextMenu(cx.root, [
24+
{
25+
label: "New tab to the right",
26+
icon: iconNew,
27+
action: () => {
28+
browser.newTabRight(props.tab);
29+
},
30+
},
31+
{
32+
label: "Reload",
33+
icon: iconRefresh,
34+
action: () => {
35+
props.tab.frame.reload();
36+
},
37+
},
38+
{
39+
label: "Duplicate",
40+
icon: iconDuplicate,
41+
action: () => {
42+
browser.newTabRight(props.tab, props.tab.url);
43+
},
44+
},
45+
{
46+
label: "Close Tab",
47+
icon: iconClose,
48+
action: () => {
49+
props.destroy();
50+
},
51+
},
52+
]);
53+
54+
cx.root.animate(
55+
[
56+
{
57+
width: "0px",
58+
},
59+
{},
60+
],
61+
{
62+
duration: 100,
63+
fill: "forwards",
64+
}
65+
);
66+
};
67+
68+
let hoverTimeout: number;
69+
70+
return (
71+
<div
72+
style="z-index: 0;"
73+
class="tab"
74+
data-id={props.id}
75+
on:mousedown={(e: MouseEvent) => {
76+
props.mousedown(e);
77+
e.stopPropagation();
78+
e.preventDefault();
79+
}}
80+
on:contextmenu={() => {
81+
if (hoverTimeout) clearTimeout(hoverTimeout);
82+
this.tooltipActive = false;
83+
}}
84+
on:transitionend={() => {
85+
cx.root.style.transition = "";
86+
cx.root.style.zIndex = "0";
87+
props.transitionend();
88+
}}
89+
on:mouseenter={() => {
90+
forceScreenshot(props.tab);
91+
if (hoverTimeout) clearTimeout(hoverTimeout);
92+
hoverTimeout = window.setTimeout(() => {
93+
this.tooltipActive = true;
94+
}, 500);
95+
}}
96+
on:mouseleave={() => {
97+
if (hoverTimeout) clearTimeout(hoverTimeout);
98+
this.tooltipActive = false;
99+
}}
100+
>
101+
<TabTooltip tab={props.tab} active={this.tooltipActive} />
102+
<div
103+
class="dragroot"
104+
style="position: unset;"
105+
on:auxclick={(e: MouseEvent) => {
106+
if (e.button === 1) {
107+
props.destroy();
108+
}
109+
}}
110+
>
111+
<div class={use(props.active).map((x) => `main ${x ? "active" : ""}`)}>
112+
{use(props.tab.icon).andThen(<img src={use(props.tab.icon)} />)}
113+
<span>{use(props.tab.title)}</span>
114+
<button
115+
class="close"
116+
on:click={(e: MouseEvent) => {
117+
e.stopPropagation();
118+
props.destroy();
119+
}}
120+
on:contextmenu={(e: MouseEvent) => {
121+
e.preventDefault();
122+
e.stopPropagation();
123+
}}
124+
>
125+
<Icon icon={iconClose} />
126+
</button>
127+
</div>
128+
{/* <div class="belowcontainer">
129+
{use(s.active).andThen(<div class="below"></div>)}
130+
</div> */}
131+
</div>
132+
</div>
133+
);
134+
}
135+
DragTab.style = css`
136+
:scope {
137+
display: inline-block;
138+
user-select: none;
139+
position: absolute;
140+
141+
--tab-active-border-width: 11px;
142+
--tab-active-border-radius: 10px;
143+
--tab-active-border-radius-neg: -10px;
144+
}
145+
146+
.main {
147+
height: 28px;
148+
min-width: 0;
149+
width: 100%;
150+
151+
color: var(--fg);
152+
153+
border-radius: 4px;
154+
padding: 7px 8px;
155+
156+
display: flex;
157+
align-items: center;
158+
gap: 8px;
159+
}
160+
.main img {
161+
width: 16px;
162+
height: 16px;
163+
}
164+
.main span {
165+
flex: 1;
166+
font-size: 12px;
167+
overflow: hidden;
168+
white-space: nowrap;
169+
text-overflow: ellipsis;
170+
height: 100%;
171+
vertical-align: center;
172+
line-height: 100%;
173+
}
174+
.main .close > * {
175+
width: 14px;
176+
height: 14px;
177+
}
178+
.close {
179+
outline: none;
180+
border: none;
181+
background: none;
182+
cursor: pointer;
183+
184+
display: flex;
185+
align-items: center;
186+
justify-content: center;
187+
color: var(--fg);
188+
189+
padding: 0;
190+
margin-left: 8px;
191+
}
192+
.close:hover {
193+
background: var(--bg20);
194+
border-radius: 0.5em;
195+
}
196+
197+
.main:not(.active):hover {
198+
transition: background 250ms;
199+
200+
background: var(--bg01);
201+
color: var(--fg);
202+
}
203+
204+
.main.active {
205+
background: var(--bg02);
206+
color: var(--fg);
207+
}
208+
209+
.belowcontainer {
210+
position: relative;
211+
}
212+
.below {
213+
position: absolute;
214+
bottom: -6px;
215+
height: 6px;
216+
width: 100%;
217+
218+
background: var(--bg);
219+
}
220+
221+
.below::before,
222+
.below::after {
223+
content: "";
224+
position: absolute;
225+
bottom: 0;
226+
227+
width: var(--tab-active-border-width);
228+
height: var(--tab-active-border-radius);
229+
230+
background: var(--bg01);
231+
}
232+
`;

0 commit comments

Comments
 (0)