Skip to content

Commit 362bfc7

Browse files
feat: shadowdom implemented, but need to allow certain stylesheets to permeate
1 parent e061135 commit 362bfc7

File tree

7 files changed

+205
-489
lines changed

7 files changed

+205
-489
lines changed

src/views/components/PopupMain.tsx

+97-87
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import useSchedules, { getActiveSchedule, replaceSchedule, switchSchedule } from
88
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
99
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
1010
import clsx from 'clsx';
11-
import React, { useState } from 'react';
11+
import React, { useEffect,useState } from 'react';
1212

1313
import CalendarIcon from '~icons/material-symbols/calendar-month';
1414
import RefreshIcon from '~icons/material-symbols/refresh';
@@ -19,6 +19,7 @@ import { SmallLogo } from './common/LogoIcon';
1919
import PopupCourseBlock from './common/PopupCourseBlock';
2020
import ScheduleDropdown from './common/ScheduleDropdown';
2121
import ScheduleListItem from './common/ScheduleListItem';
22+
import { ShadowDOM } from './injected/CourseCatalogInjectedPopup/ShadowDOMPortal';
2223

2324
/**
2425
* Renders the main popup component.
@@ -27,6 +28,13 @@ import ScheduleListItem from './common/ScheduleListItem';
2728
export default function PopupMain(): JSX.Element {
2829
const [activeSchedule, schedules] = useSchedules();
2930
const [isRefreshing, setIsRefreshing] = useState(false);
31+
const [parentElement, setParentElement] = useState<HTMLElement>(document.createElement('div'));
32+
33+
useEffect(() => {
34+
let parent = document.getElementById('root');
35+
setParentElement(parent);
36+
console.log(parent);
37+
}, []);
3038

3139
const handleOpenOptions = async () => {
3240
const url = chrome.runtime.getURL('/options.html');
@@ -39,97 +47,99 @@ export default function PopupMain(): JSX.Element {
3947
};
4048

4149
return (
42-
<ExtensionRoot>
43-
<div className='h-screen max-h-full flex flex-col bg-white'>
44-
<div className='p-5 py-3.5'>
45-
<div className='flex items-center justify-between bg-white'>
46-
<SmallLogo />
47-
<div className='flex items-center gap-2.5'>
48-
<button className='bg-ut-burntorange px-2 py-1.25 btn' onClick={handleCalendarOpenOnClick}>
49-
<CalendarIcon className='size-6 text-white' />
50-
</button>
51-
<button className='bg-transparent px-2 py-1.25 btn' onClick={handleOpenOptions}>
52-
<SettingsIcon className='size-6 color-ut-black' />
53-
</button>
50+
<ShadowDOM parentElement={parentElement}>
51+
<ExtensionRoot>
52+
<div className='utrp_shadow h-screen max-h-full flex flex-col bg-white'> {/* utrp_shadow creates a ShadowDOM subtree to protect CSS styles */}
53+
<div className='p-5 py-3.5'>
54+
<div className='flex items-center justify-between bg-white'>
55+
<SmallLogo />
56+
<div className='flex items-center gap-2.5'>
57+
<button className='bg-ut-burntorange px-2 py-1.25 btn' onClick={handleCalendarOpenOnClick}>
58+
<CalendarIcon className='size-6 text-white' />
59+
</button>
60+
<button className='bg-transparent px-2 py-1.25 btn' onClick={handleOpenOptions}>
61+
<SettingsIcon className='size-6 color-ut-black' />
62+
</button>
63+
</div>
5464
</div>
5565
</div>
56-
</div>
57-
<Divider orientation='horizontal' size='100%' />
58-
<div className='px-5 pb-2.5 pt-3.75'>
59-
<ScheduleDropdown>
60-
<List
61-
draggables={schedules}
62-
itemKey={schedule => schedule.id}
63-
onReordered={reordered => {
64-
const activeSchedule = getActiveSchedule();
65-
const activeIndex = reordered.findIndex(s => s.id === activeSchedule.id);
66+
<Divider orientation='horizontal' size='100%' />
67+
<div className='px-5 pb-2.5 pt-3.75'>
68+
<ScheduleDropdown>
69+
<List
70+
draggables={schedules}
71+
itemKey={schedule => schedule.id}
72+
onReordered={reordered => {
73+
const activeSchedule = getActiveSchedule();
74+
const activeIndex = reordered.findIndex(s => s.id === activeSchedule.id);
6675

67-
// don't care about the promise
68-
UserScheduleStore.set('schedules', reordered);
69-
UserScheduleStore.set('activeIndex', activeIndex);
70-
}}
71-
gap={10}
72-
>
73-
{(schedule, handleProps) => (
74-
<ScheduleListItem
75-
schedule={schedule}
76-
onClick={() => {
77-
switchSchedule(schedule.id);
78-
}}
79-
dragHandleProps={handleProps}
80-
/>
81-
)}
82-
</List>
83-
</ScheduleDropdown>
84-
</div>
85-
<div className='flex-1 self-stretch overflow-y-auto px-5'>
86-
{activeSchedule?.courses?.length > 0 && (
87-
<List
88-
draggables={activeSchedule.courses}
89-
onReordered={reordered => {
90-
activeSchedule.courses = reordered;
91-
replaceSchedule(getActiveSchedule(), activeSchedule);
92-
}}
93-
itemKey={e => e.uniqueId}
94-
gap={10}
95-
>
96-
{(course, handleProps) => (
97-
<PopupCourseBlock
98-
key={course.uniqueId}
99-
course={course}
100-
colors={course.colors}
101-
dragHandleProps={handleProps}
102-
/>
103-
)}
104-
</List>
105-
)}
106-
</div>
107-
108-
<div className='w-full flex flex-col items-center gap-1.25 p-5 pt-3.75'>
109-
<div className='flex gap-2.5'>
110-
<CourseStatus status='WAITLISTED' size='mini' />
111-
<CourseStatus status='CLOSED' size='mini' />
112-
<CourseStatus status='CANCELLED' size='mini' />
76+
// don't care about the promise
77+
UserScheduleStore.set('schedules', reordered);
78+
UserScheduleStore.set('activeIndex', activeIndex);
79+
}}
80+
gap={10}
81+
>
82+
{(schedule, handleProps) => (
83+
<ScheduleListItem
84+
schedule={schedule}
85+
onClick={() => {
86+
switchSchedule(schedule.id);
87+
}}
88+
dragHandleProps={handleProps}
89+
/>
90+
)}
91+
</List>
92+
</ScheduleDropdown>
93+
</div>
94+
<div className='flex-1 self-stretch overflow-y-auto px-5'>
95+
{activeSchedule?.courses?.length > 0 && (
96+
<List
97+
draggables={activeSchedule.courses}
98+
onReordered={reordered => {
99+
activeSchedule.courses = reordered;
100+
replaceSchedule(getActiveSchedule(), activeSchedule);
101+
}}
102+
itemKey={e => e.uniqueId}
103+
gap={10}
104+
>
105+
{(course, handleProps) => (
106+
<PopupCourseBlock
107+
key={course.uniqueId}
108+
course={course}
109+
colors={course.colors}
110+
dragHandleProps={handleProps}
111+
/>
112+
)}
113+
</List>
114+
)}
113115
</div>
114-
<div className='inline-flex items-center self-center gap-1'>
115-
<Text variant='mini' className='text-ut-gray'>
116-
DATA LAST UPDATED: {getUpdatedAtDateTimeString(activeSchedule.updatedAt)}
117-
</Text>
118-
<button
119-
className='h-4 w-4 bg-transparent p-0 btn'
120-
onClick={() => {
121-
setIsRefreshing(true);
122-
}}
123-
>
124-
<RefreshIcon
125-
className={clsx('h-4 w-4 text-ut-black animate-duration-800', {
126-
'animate-spin': isRefreshing,
127-
})}
128-
/>
129-
</button>
116+
117+
<div className='w-full flex flex-col items-center gap-1.25 p-5 pt-3.75'>
118+
<div className='flex gap-2.5'>
119+
<CourseStatus status='WAITLISTED' size='mini' />
120+
<CourseStatus status='CLOSED' size='mini' />
121+
<CourseStatus status='CANCELLED' size='mini' />
122+
</div>
123+
<div className='inline-flex items-center self-center gap-1'>
124+
<Text variant='mini' className='text-ut-gray'>
125+
DATA LAST UPDATED: {getUpdatedAtDateTimeString(activeSchedule.updatedAt)}
126+
</Text>
127+
<button
128+
className='h-4 w-4 bg-transparent p-0 btn'
129+
onClick={() => {
130+
setIsRefreshing(true);
131+
}}
132+
>
133+
<RefreshIcon
134+
className={clsx('h-4 w-4 text-ut-black animate-duration-800', {
135+
'animate-spin': isRefreshing,
136+
})}
137+
/>
138+
</button>
139+
</div>
130140
</div>
131141
</div>
132-
</div>
133-
</ExtensionRoot>
142+
</ExtensionRoot>
143+
</ShadowDOM>
134144
);
135145
}

src/views/components/common/ExtensionRoot/ExtensionRoot.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'uno.css';
44
import clsx from 'clsx';
55
import React from 'react';
66

7+
import ShadowRootAttacher from '../ShadowDOM/ShadowDOM';
78
import styles from './ExtensionRoot.module.scss';
89

910
interface Props {
@@ -16,10 +17,10 @@ interface Props {
1617
*/
1718
export default function ExtensionRoot(props: React.PropsWithChildren<Props>): JSX.Element {
1819
return (
19-
<React.StrictMode>
20-
<div className={clsx(styles.extensionRoot, props.className)} data-testid={props.testId}>
21-
{props.children}
22-
</div>
23-
</React.StrictMode>
20+
21+
<><div className={clsx(styles.extensionRoot, props.className)} data-testid={props.testId}>
22+
{props.children}
23+
</div></>
24+
2425
);
2526
}

0 commit comments

Comments
 (0)