148 |
${id}
149 |
150 |
155 |
160 |
165 |
166 |
`;
167 |
168 | const [up, down, remove] = container.querySelectorAll("button");
169 |
170 | up.onclick = () => posCallback(container, -1);
171 | down.onclick = () => posCallback(container, 1);
172 | remove.onclick = () => removeCallback(container);
173 |
174 | return container;
175 | }
176 |
--------------------------------------------------------------------------------
/CustomApps/reddit/SortBox.js:
--------------------------------------------------------------------------------
1 | class SortBox extends react.Component {
2 | constructor(props) {
3 | super(props);
4 | this.sortByOptions = [
5 | { key: "hot", value: "Hot" },
6 | { key: "new", value: "New" },
7 | { key: "top", value: "Top" },
8 | { key: "rising", value: "Rising" },
9 | { key: "controversial", value: "Controversial" },
10 | ];
11 | this.sortTimeOptions = [
12 | { key: "hour", value: "Hour" },
13 | { key: "day", value: "Day" },
14 | { key: "week", value: "Week" },
15 | { key: "month", value: "Month" },
16 | { key: "year", value: "Year" },
17 | { key: "all", value: "All" },
18 | ];
19 | }
20 |
21 | render() {
22 | const sortBySelected = this.sortByOptions.filter((a) => a.key === sortConfig.by)[0];
23 | const sortTimeSelected = this.sortTimeOptions.filter((a) => a.key === sortConfig.time)[0];
24 |
25 | return react.createElement(
26 | "div",
27 | {
28 | className: "reddit-sort-bar",
29 | },
30 | react.createElement(
31 | "div",
32 | {
33 | className: "reddit-sort-container",
34 | },
35 | react.createElement(OptionsMenu, {
36 | options: this.sortByOptions,
37 | onSelect: (by) => this.props.onChange(by, null),
38 | selected: sortBySelected,
39 | }),
40 | !!sortConfig.by.match(/top|controversial/) &&
41 | react.createElement(OptionsMenu, {
42 | options: this.sortTimeOptions,
43 | onSelect: (time) => this.props.onChange(null, time),
44 | selected: sortTimeSelected,
45 | })
46 | )
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/CustomApps/reddit/TabBar.js:
--------------------------------------------------------------------------------
1 | class TabBarItem extends react.Component {
2 | render() {
3 | return react.createElement(
4 | "li",
5 | {
6 | className: "reddit-tabBar-headerItem",
7 | onClick: (event) => {
8 | event.preventDefault();
9 | this.props.switchTo(this.props.item.key);
10 | },
11 | },
12 | react.createElement(
13 | "a",
14 | {
15 | "aria-current": "page",
16 | className: `reddit-tabBar-headerItemLink ${this.props.item.active ? "reddit-tabBar-active" : ""}`,
17 | draggable: "false",
18 | href: "",
19 | },
20 | react.createElement(
21 | "span",
22 | {
23 | className: "main-type-mestoBold",
24 | },
25 | this.props.item.value
26 | )
27 | )
28 | );
29 | }
30 | }
31 |
32 | const TabBarMore = react.memo(({ items, switchTo }) => {
33 | const activeItem = items.find((item) => item.active);
34 |
35 | return react.createElement(
36 | "li",
37 | {
38 | className: `reddit-tabBar-headerItem ${activeItem ? "reddit-tabBar-active" : ""}`,
39 | },
40 | react.createElement(OptionsMenu, {
41 | options: items,
42 | onSelect: switchTo,
43 | selected: activeItem,
44 | defaultValue: "More",
45 | bold: true,
46 | })
47 | );
48 | });
49 |
50 | const TopBarContent = ({ links, activeLink, switchCallback }) => {
51 | const resizeHost = document.querySelector(
52 | ".Root__main-view .os-resize-observer-host, .Root__main-view .os-size-observer, .Root__main-view .main-view-container__scroll-node"
53 | );
54 | const [windowSize, setWindowSize] = useState(resizeHost.clientWidth);
55 | const resizeHandler = () => setWindowSize(resizeHost.clientWidth);
56 |
57 | useEffect(() => {
58 | const observer = new ResizeObserver(resizeHandler);
59 | observer.observe(resizeHost);
60 | return () => {
61 | observer.disconnect();
62 | };
63 | }, [resizeHandler]);
64 |
65 | return react.createElement(
66 | TabBarContext,
67 | null,
68 | react.createElement(TabBar, {
69 | className: "queue-queueHistoryTopBar-tabBar",
70 | links,
71 | activeLink,
72 | windowSize,
73 | switchCallback,
74 | })
75 | );
76 | };
77 |
78 | const TabBarContext = ({ children }) => {
79 | return reactDOM.createPortal(
80 | react.createElement(
81 | "div",
82 | {
83 | className: "main-topBar-topbarContent",
84 | },
85 | children
86 | ),
87 | document.querySelector(".main-topBar-topbarContentWrapper")
88 | );
89 | };
90 |
91 | const TabBar = react.memo(({ links, activeLink, switchCallback, windowSize = Number.POSITIVE_INFINITY }) => {
92 | const tabBarRef = react.useRef(null);
93 | const [childrenSizes, setChildrenSizes] = useState([]);
94 | const [availableSpace, setAvailableSpace] = useState(0);
95 | const [droplistItem, setDroplistItems] = useState([]);
96 |
97 | const options = links.map((key) => {
98 | const active = key === activeLink;
99 | return { key, value: key, active };
100 | });
101 |
102 | useEffect(() => {
103 | if (!tabBarRef.current) return;
104 | setAvailableSpace(tabBarRef.current.clientWidth);
105 | }, [windowSize]);
106 |
107 | useEffect(() => {
108 | if (!tabBarRef.current) return;
109 |
110 | const children = Array.from(tabBarRef.current.children);
111 | const tabbarItemSizes = children.map((child) => child.clientWidth);
112 |
113 | setChildrenSizes(tabbarItemSizes);
114 | }, [links]);
115 |
116 | useEffect(() => {
117 | if (!tabBarRef.current) return;
118 |
119 | const totalSize = childrenSizes.reduce((a, b) => a + b, 0);
120 |
121 | // Can we render everything?
122 | if (totalSize <= availableSpace) {
123 | setDroplistItems([]);
124 | return;
125 | }
126 |
127 | // The `More` button can be set to _any_ of the children. So we
128 | // reserve space for the largest item instead of always taking
129 | // the last item.
130 | const viewMoreButtonSize = Math.max(...childrenSizes);
131 |
132 | // Figure out how many children we can render while also showing
133 | // the More button
134 | const itemsToHide = [];
135 | let stopWidth = viewMoreButtonSize;
136 |
137 | childrenSizes.forEach((childWidth, i) => {
138 | if (availableSpace >= stopWidth + childWidth) {
139 | stopWidth += childWidth;
140 | } else {
141 | itemsToHide.push(i);
142 | }
143 | });
144 |
145 | setDroplistItems(itemsToHide);
146 | }, [availableSpace, childrenSizes]);
147 |
148 | return react.createElement(
149 | "nav",
150 | {
151 | className: "reddit-tabBar reddit-tabBar-nav",
152 | },
153 | react.createElement(
154 | "ul",
155 | {
156 | className: "reddit-tabBar-header",
157 | ref: tabBarRef,
158 | },
159 | options
160 | .filter((_, id) => !droplistItem.includes(id))
161 | .map((item) =>
162 | react.createElement(TabBarItem, {
163 | item,
164 | switchTo: switchCallback,
165 | })
166 | ),
167 | droplistItem.length || childrenSizes.length === 0
168 | ? react.createElement(TabBarMore, {
169 | items: droplistItem.map((i) => options[i]).filter(Boolean),
170 | switchTo: switchCallback,
171 | })
172 | : null
173 | )
174 | );
175 | });
176 |
--------------------------------------------------------------------------------
/CustomApps/reddit/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Reddit",
3 | "icon": "