11// Defines a system of "slots" which modules can use to render interface
2- // elements within the page. Slot locations are standardized for consumers (e.g.
3- // a module says it wants to display a button next to comment author usernames)
2+ // elements within the page. Slot types are standardized for consumers (e.g. a
3+ // module says it wants to display a button next to comment author usernames)
44// and their actual position in the DOM is controlled by platform-specific
55// observers responding to changes in the page and dynamically creating React
66// roots which this code then populates with the appropriate contents.
@@ -17,124 +17,190 @@ import modmailObserver from './modmail';
1717import oldRedditObserver from './oldreddit' ;
1818import shredditObserver from './shreddit' ;
1919
20- // NOMERGE: document all of these
20+ /** Basic information about a subreddit. */
2121interface PlatformSlotDetailsSubreddit {
22+ /** The subreddit's fullname, beginning with ``. */
2223 fullname ?: string ;
24+ /** The name of the subreddit */
2325 name : string ;
2426}
2527
28+ /** Basic information about a user. */
2629export type PlatformSlotDetailsUser = {
30+ /** If `true`, this is a deleted user. */
2731 deleted : true ;
2832} | {
33+ /** If `true`, this is a deleted user. */
2934 deleted : false ;
35+ /** The user's fullname, starting with ``. */
3036 fullname ?: string ;
37+ /** The user's username. */
3138 name : string ;
3239} ;
3340
41+ /** Basic information about a submission. */
3442interface PlatformSlotDetailsSubmission {
43+ /** The submission's fullname, beginning with ``. */
3544 fullname : string ;
3645}
3746
47+ /** Basic information about a comment. */
3848interface PlatformSlotDetailsComment {
49+ /** The comment's fullname, beginning with ``. */
3950 fullname : string ;
4051}
4152
4253// Slot names and the type of associated contextual information
43- // NOMERGE: document
54+
55+ /** Contextual information provided to consumers of each type of slot. */
4456export interface PlatformSlotDetails {
57+ /** Details for a submission author slot. */
4558 submissionAuthor : {
59+ /** The author of this submission */
4660 user : PlatformSlotDetailsUser ;
61+ /** The submission */
4762 submission ?: PlatformSlotDetailsSubmission ;
63+ /** The subreddit where this submission was posted */
4864 subreddit : PlatformSlotDetailsSubreddit ;
65+ // /** The type of distinguish on the submission, if any */
4966 // distinguishType: null | 'moderator' | 'employee' | 'alumnus';
50- // stickied: boolean;
67+ // /** The sticky slot populated by the submission, if any */
68+ // stickied: false | 1 | 2;
5169 } ;
5270 commentAuthor : {
71+ /** The author of the comment */
5372 user : PlatformSlotDetailsUser ;
73+ /** The comment */
5474 comment : PlatformSlotDetailsComment ;
75+ /** The parent submission the comment was left under */
5576 submission ?: PlatformSlotDetailsSubmission ;
77+ /** The subreddit where the comment was posted */
5678 subreddit : PlatformSlotDetailsSubreddit ;
79+ // /** The type of distinguish on the comment, if any */
5780 // distinguished: boolean;
81+ // /** Whether the comment is stickied */
5882 // stickied: boolean;
5983 } ;
6084 modmailAuthor : {
85+ /** The author of the message */
6186 user : PlatformSlotDetailsUser ;
87+ /** The subreddit that initially received this message's thread */
6288 subreddit : PlatformSlotDetailsSubreddit ;
89+ /** The thread this message is in */
6390 thread : { fullname : string } ;
91+ /** The message */
6492 message : { fullname : string } ;
93+ // /** Whether the author is a moderator */
6594 // authorIsModerator: boolean;
95+ // /** Whether this message was sent "as the subreddit" (with username hidden) */
6696 // repliedAsSubreddit: boolean;
6797 } ;
6898 userHovercard : {
99+ /** The user */
69100 user : PlatformSlotDetailsUser ;
101+ /**
102+ * The subreddit of the content the hovercard was triggered from. For
103+ * example if the hovercard was triggered from an author name on a
104+ * submission, this would be the subreddit it was submitted to; if the
105+ * hovercard is triggered on a user in a modmail thread, this would be
106+ * the subreddit that received the thread.
107+ */
70108 subreddit : PlatformSlotDetailsSubreddit ;
109+ /**
110+ * The fullname of the submission, comment, modmail thread, etc. the
111+ * hovercard was triggered from
112+ */
71113 contextFullname ?: string ;
72114 } ;
73115}
74- export type PlatformSlotLocation = keyof PlatformSlotDetails ;
116+ /**
117+ * A slot type. Describes a location on the page where slot contents can be
118+ * rendered (e.g. `submissionAuthor` is a slot type that's rendered next to the
119+ * usernames of submission authors).
120+ */
121+ export type PlatformSlotType = keyof PlatformSlotDetails ;
75122
76123// Consumer code (used by toolbox modules)
77124
78125// A consumer of a particular slot location which gets appropriate context and
79126// returns React content to be rendered in the slot
80- export type PlatformSlotContent < Location extends keyof PlatformSlotDetails > = ComponentType < {
81- details : PlatformSlotDetails [ Location ] ;
82- location : Location ;
127+ export type PlatformSlotContent < SlotType extends keyof PlatformSlotDetails > = ComponentType < {
128+ /**
129+ * Contextual details about the content the slot is attached to. Different
130+ * slot types provide different information in this object.
131+ */
132+ details : PlatformSlotDetails [ SlotType ] ;
133+ /** The type of slot the component is currently being populated into. */
134+ slotType : SlotType ;
83135} > ;
84136
85137// Map of slot locations to consumers of the slot
86138const slotConsumers : {
87139 [ K in keyof PlatformSlotDetails ] ?: PlatformSlotContent < K > [ ] ;
88140} = Object . create ( null ) ;
89141
90- // NOMERGE: document
91- export function renderInSlots < K extends keyof PlatformSlotDetails > ( locations : K [ ] , render : PlatformSlotContent < K > ) {
92- if ( ! Array . isArray ( locations ) ) {
93- locations = [ ] ;
142+ /**
143+ * Provide a consumer for one or more slot types. Whenever any of the `slots`
144+ * appears on the page, the given component/renderer will be used to populate
145+ * the slot (alongside any other consumers of the same slot type).
146+ * @param slots An array of slots where the given component should be rendered
147+ * @param render A React function component/render function that will be
148+ * rendered in those slots. Props are passed which inform the component which
149+ * type of slot it's currently being rendered in, and contextual information
150+ * about the slot's surroundings.
151+ */
152+ export function renderInSlots < K extends keyof PlatformSlotDetails > ( slots : K [ ] , render : PlatformSlotContent < K > ) {
153+ if ( ! Array . isArray ( slots ) ) {
154+ slots = [ ] ;
94155 }
95- for ( const location of locations ) {
96- if ( ! slotConsumers [ location ] ) {
97- slotConsumers [ location ] = [ ] ;
156+ for ( const slot of slots ) {
157+ if ( ! slotConsumers [ slot ] ) {
158+ slotConsumers [ slot ] = [ ] ;
98159 }
99- slotConsumers [ location ] ?. push ( render ) ;
160+ slotConsumers [ slot ] ?. push ( render ) ;
100161 }
101162}
102163
103164// Observer code (used by platform-specific observers in this directory)
104165
105- // NOMERGE: document
166+ /**
167+ * A platform observer is a function responsible creating slot instances and
168+ * attaching them to the page in the appropriate locations for a specific
169+ * platform. It is called once when Toolbox starts and receives a function which
170+ * creates slot instances, which it then inserts into the DOM.
171+ */
106172export type PlatformObserver = (
107173 /**
108174 * Creates a React root for a slot which will be populated with the
109175 * appropriate contents. Observers are responsible for calling this function
110176 * and inserting the resulting element into the DOM wherever the slot should
111177 * be rendered.
112178 */
113- createRenderer : < Location extends keyof PlatformSlotDetails > (
114- location : Location ,
115- details : PlatformSlotDetails [ Location ] ,
179+ createRenderer : < SlotType extends keyof PlatformSlotDetails > (
180+ slotType : SlotType ,
181+ details : PlatformSlotDetails [ SlotType ] ,
116182 ) => HTMLElement ,
117183) => void ;
118184
119185// the actual `createRenderer` function observers get - returns a new react root
120186// which will contain all the contents different modules have registered for the
121- // given slot location
187+ // given slot type
122188// NOTE: Exported because tbui builders need to manually emit their own slots.
123189// Should we just import this from the platform-specific bits instead of
124190// passing this function in to them?
125- export const createRenderer = < K extends keyof PlatformSlotDetails > ( location : K , details : PlatformSlotDetails [ K ] ) =>
191+ export const createRenderer = < K extends keyof PlatformSlotDetails > ( slotType : K , details : PlatformSlotDetails [ K ] ) =>
126192 reactRenderer (
127193 < div
128194 className = 'tb-platform-slot'
129195 style = { { display : 'inline-flex' } } // FIXME: do this in CSS
130- data-location = { location }
196+ data-slot-type = { slotType }
131197 >
132198 { /* TODO: Do we want to do anything more sophisticated here? */ }
133- { slotConsumers [ location ] ?. map ( ( Component , i ) => (
199+ { slotConsumers [ slotType ] ?. map ( ( Component , i ) => (
134200 < Component
135201 key = { i }
136202 details = { details }
137- location = { location }
203+ slotType = { slotType }
138204 />
139205 ) ) }
140206 </ div > ,
0 commit comments