1010import store from './store'
1111import { cancelButton } from './widgets'
1212import { label } from './utils'
13+ import { NamedNode } from 'rdflib'
1314
14- // options.subject
15- // options.orientation 0 top, 1 left, 2 bottom, 3 right
16- // options.showMain(div, subject) function to show subject in div when tab selected
17- // options.renderTabSettings function(subject, domContainer)
18- // options.renderTabSettings like showMain but when user has held Alt down
19- // options.onClose if given, will present a cancelButton next to tabs that calls this optional method
20- //
15+ type TabWidgetOptions = {
16+ backgroundColor ?: string
17+ dom : HTMLDocument
18+ items ?: Array < NamedNode >
19+ onClose ?: ( event : Event ) => void
20+ ordered ?: boolean
21+ orientation ?: '0' | '1' | '2' | '3'
22+ predicate ?: NamedNode
23+ renderMain ?: ( bodyMain : HTMLElement , subject : NamedNode ) => void
24+ renderTab ?: ( tabDiv : HTMLDivElement , subject : NamedNode ) => void
25+ renderTabSettings ?: ( bodyMain : ContainerElement , subject : NamedNode ) => void
26+ selectedTab ?: string
27+ startEmpty ?: boolean
28+ subject : NamedNode
29+ }
30+
31+ export class TabWidgetElement extends HTMLElement {
32+ bodyContainer ?: HTMLElement
33+ refresh ?: ( ) => void
34+ tabContainer ?: HTMLElement
35+ }
36+
37+ class ContainerElement extends HTMLElement {
38+ asSettings ?: boolean
39+ }
2140
22- export function tabWidget ( options ) {
23- const kb = store
41+ class TabElement extends HTMLElement {
42+ bodyTR ?: HTMLElement
43+ subject ?: NamedNode
44+ }
45+
46+ export function tabWidget ( options : TabWidgetOptions ) {
2447 const subject = options . subject
2548 const dom = options . dom
2649 const orientation = parseInt ( options . orientation || '0' )
2750 const backgroundColor = options . backgroundColor || '#ddddcc'
28- let color
2951 const flipped = orientation & 2
3052 const vertical = orientation & 1
3153 const onClose = options . onClose
3254
33- const isLight = function ( x ) {
34- let total = 0
35- for ( let i = 0 ; i < 3 ; i ++ ) {
36- total += parseInt ( x . slice ( i * 2 + 1 , i * 2 + 3 ) , 16 )
37- }
38- return total > 128 * 3
39- }
40- const colorBlend = function ( a , b , mix ) {
41- let ca , cb , res
42- let str = '#'
43- const hex = '0123456789abcdef'
44- for ( let i = 0 ; i < 3 ; i ++ ) {
45- ca = parseInt ( a . slice ( i * 2 + 1 , i * 2 + 3 ) , 16 )
46- cb = parseInt ( b . slice ( i * 2 + 1 , i * 2 + 3 ) , 16 )
47- res = ca * ( 1.0 - mix ) + cb * mix // @@@ rounding
48- const res2 = parseInt ( ( '' + res ) . split ( '.' ) [ 0 ] ) // @@ ugh
49- const h = parseInt ( ( '' + res2 / 16 ) . split ( '.' ) [ 0 ] ) // @@ ugh
50- const l = parseInt ( ( '' + ( res2 % 16 ) ) . split ( '.' ) [ 0 ] ) // @@ ugh
51- str += hex [ h ] + hex [ l ]
52- }
53- // console.log('Blending colors ' + a + ' with ' + mix + ' of ' + b + ' to give ' + str)
54- return str
55- }
56-
57- let selectedColor
58- if ( isLight ( backgroundColor ) ) {
59- selectedColor = colorBlend ( backgroundColor , '#ffffff' , 0.3 )
60- color = '#000000'
61- } else {
62- selectedColor = colorBlend ( backgroundColor , '#000000' , 0.3 )
63- color = '#ffffff'
64- }
55+ const [ selectedColor , color ] = getColors ( backgroundColor )
6556 const bodyMainStyle = `flex: 2; width: auto; height: 100%; border: 0.1em; border-style: solid; border-color: ${ selectedColor } ; padding: 1em;`
66-
67- /*
68- 'resize: both; overflow: scroll; margin:0; border: 0.1em; border-style: solid; border-color: ' +
69- selectedColor +
70- '; padding: 1em; min-width: 30em; min-height: 450px; width:100%;'
71- */
72- const rootElement = dom . createElement ( 'div' ) // 20200117a
57+ const rootElement : TabWidgetElement = dom . createElement ( 'div' ) // 20200117a
7358
7459 rootElement . setAttribute ( 'style' , 'display: flex; height: 100%; width: 100%; flex-direction: ' +
7560 ( vertical ? 'row' : 'column' ) + ( flipped ? '-reverse;' : ';' ) )
@@ -81,64 +66,76 @@ export function tabWidget (options) {
8166
8267 mainElement . setAttribute ( 'style' , 'margin: 0; width:100%; height: 100%;' ) // override tabbedtab.css
8368 const tabContainer = navElement . appendChild ( dom . createElement ( 'ul' ) )
84- tabContainer . setAttribute ( 'style' , 'list-style-type: none;' + // No bullet please ...
85- 'display: flex; height: 100%; width: 100%; flex-direction: ' +
86- ( vertical ? 'column' : 'row' ) ) // + (flipped ? '-reverse;' : ';')
87- // Never flip the direction of readuing the tabs, assuming people read left to right and top to bottom
69+ tabContainer . setAttribute ( 'style' , `
70+ list-style-type: none;
71+ display: flex;
72+ height: 100%;
73+ width: 100%;
74+ padding: 0;
75+ flex-direction: ${ ( vertical ? 'column' : 'row' ) }
76+ ` )
8877
8978 const tabElement = 'li'
9079
9180 const bodyContainer = mainElement // .appendChild(dom.createElement('table'))
9281 rootElement . tabContainer = tabContainer // ussed by caller
9382 rootElement . bodyContainer = bodyContainer
9483
95- const getItems = function ( ) {
96- if ( options . items ) return options . items
97- if ( options . ordered !== false ) {
98- // default to true
99- const list = kb . the ( subject , options . predicate )
100- return list . elements
101- } else {
102- return kb . each ( subject , options . predicate )
103- }
104- }
105-
106- let corners = [ '2em' , '2em' , '0' , '0' ] // top left, TR, BR, BL
107- corners = corners . concat ( corners ) . slice ( orientation , orientation + 4 )
108- const cornersStyle = 'border-radius: ' + corners . join ( ' ' ) + ';'
84+ const corners = [ '2em' , '2em' , '0' , '0' ] // top left, TR, BR, BL
85+ const cornersPrepped = corners . concat ( corners ) . slice ( orientation , orientation + 4 )
86+ const cornersStyle = 'border-radius: ' + cornersPrepped . join ( ' ' ) + ';'
10987
110- let margins = [ '0.3em' , '0.3em' , '0' , '0.3em' ] // top, right, bottom, left
111- margins = margins . concat ( margins ) . slice ( orientation , orientation + 4 )
112- const marginsStyle = 'margin: ' + margins . join ( ' ' ) + ';'
88+ const margins = [ '0.3em' , '0.3em' , '0' , '0.3em' ] // top, right, bottom, left
89+ const marginsPrepped = margins . concat ( margins ) . slice ( orientation , orientation + 4 )
90+ const marginsStyle = 'margin: ' + marginsPrepped . join ( ' ' ) + ';'
11391
114- let tabStyle = cornersStyle + 'padding: 0.7em; max-width: 20em;' // border: 0.05em 0 0.5em 0.05em; border-color: grey;
115- tabStyle += 'color: ' + color + ';'
116- const unselectedStyle =
117- tabStyle +
118- 'opacity: 50%; margin: 0.3em; background-color: ' +
119- backgroundColor +
120- ';' // @@ rotate border
92+ const tabStyle = cornersStyle + `padding: 0.7em; max-width: 20em;color: ${ color } ;` // border: 0.05em 0 0.5em 0.05em; border-color: grey;
93+ const unselectedStyle = `${ tabStyle } opacity: 50%; margin: 0.3em; background-color: ${ backgroundColor } ;` // @@ rotate border
12194 const selectedStyle = tabStyle + marginsStyle + ' background-color: ' + selectedColor + ';'
12295 const shownStyle = 'height: 100%; width: 100%;'
12396 const hiddenStyle = shownStyle + 'display: none;'
97+ rootElement . refresh = sync
98+ sync ( )
12499
125- const resetTabStyle = function ( ) {
126- for ( let i = 0 ; i < tabContainer . children . length ; i ++ ) {
127- const tab = tabContainer . children [ i ]
128- if ( tab . classList . contains ( 'unstyled' ) ) {
129- continue
130- }
131- tab . firstChild . setAttribute ( 'style' , unselectedStyle )
100+ // From select-tabs branch by hand
101+ if ( ! options . startEmpty && tabContainer . children . length && options . selectedTab ) {
102+ const selectedTab = Array . from ( tabContainer . children )
103+ . map ( tab => tab . firstChild as HTMLElement )
104+ . find ( tab => tab . dataset . name === options . selectedTab )
105+ const tab = selectedTab || tabContainer . children [ 0 ] . firstChild as HTMLButtonElement
106+ tab . click ( )
107+ } else if ( ! options . startEmpty ) {
108+ ( tabContainer . children [ 0 ] . firstChild as HTMLButtonElement ) . click ( ) // Open first tab
109+ }
110+ return rootElement
111+
112+ function addCancelButton ( tabContainer ) {
113+ if ( tabContainer . dataset . onCloseSet ) {
114+ // @@ TODO: this is only here to make the tests work
115+ // Discussion at https://github.com/solid/solid-ui/pull/110#issuecomment-527080663
116+ const existingCancelButton = tabContainer . querySelector ( '.unstyled' )
117+ tabContainer . removeChild ( existingCancelButton )
132118 }
119+ const extraTab = dom . createElement ( tabElement )
120+ extraTab . classList . add ( 'unstyled' )
121+ const tabCancelButton = cancelButton ( dom , onClose )
122+ extraTab . appendChild ( tabCancelButton )
123+ tabContainer . appendChild ( extraTab )
124+ tabContainer . dataset . onCloseSet = 'true'
133125 }
134- const resetBodyStyle = function ( ) {
135- for ( let i = 0 ; i < bodyContainer . children . length ; i ++ ) {
136- bodyContainer . children [ i ] . setAttribute ( 'style' , hiddenStyle )
126+
127+ function getItems ( ) : Array < NamedNode > {
128+ if ( options . items ) return options . items
129+ if ( options . ordered !== false ) {
130+ // default to true
131+ return store . the ( subject , options . predicate ) . elements
132+ } else {
133+ return store . each ( subject , options . predicate )
137134 }
138135 }
139136
140- const makeNewSlot = function ( item ) {
141- const ele = dom . createElement ( tabElement )
137+ function makeNewSlot ( item : NamedNode ) {
138+ const ele : TabElement = dom . createElement ( tabElement )
142139 ele . subject = item
143140 const div = ele . appendChild ( dom . createElement ( 'div' ) )
144141 div . setAttribute ( 'style' , unselectedStyle )
@@ -149,23 +146,24 @@ export function tabWidget (options) {
149146 resetBodyStyle ( )
150147 }
151148 div . setAttribute ( 'style' , selectedStyle )
149+ if ( ! ele . bodyTR ) return
152150 ele . bodyTR . setAttribute ( 'style' , shownStyle )
153- let bodyMain = ele . bodyTR . firstChild
151+ let bodyMain = ele . bodyTR . children [ 0 ] as ContainerElement
154152 if ( ! bodyMain ) {
155153 bodyMain = ele . bodyTR . appendChild ( dom . createElement ( 'main' ) )
156154 bodyMain . setAttribute ( 'style' , bodyMainStyle )
157155 }
158- if ( options . renderTabSettings && e . altKey ) {
159- if ( bodyMain . asSetttings !== true ) {
156+ if ( options . renderTabSettings && e . altKey && ele . subject ) {
157+ if ( bodyMain . asSettings !== true ) {
160158 bodyMain . innerHTML = 'loading settings ...' + item
161159 options . renderTabSettings ( bodyMain , ele . subject )
162- bodyMain . asSetttings = true
160+ bodyMain . asSettings = true
163161 }
164- } else {
165- if ( bodyMain . asSetttings !== false ) {
162+ } else if ( options . renderMain && ele . subject ) {
163+ if ( bodyMain . asSettings !== false ) {
166164 bodyMain . innerHTML = 'loading item ...' + item
167165 options . renderMain ( bodyMain , ele . subject )
168- bodyMain . asSetttings = false
166+ bodyMain . asSettings = false
169167 }
170168 }
171169 } )
@@ -179,16 +177,16 @@ export function tabWidget (options) {
179177 }
180178
181179 // @@ Use common one from utils?
182- const orderedSync = function ( ) {
180+ function orderedSync ( ) {
183181 const items = getItems ( )
184182 if ( ! vertical ) {
185183 // mainElement.setAttribute('colspan', items.length + (onClose ? 1 : 0))
186184 }
187- let slot , i , j , left , right
185+ let slot : TabElement , i , j , left , right
188186 let differ = false
189187 // Find how many match at each end
190188 for ( left = 0 ; left < tabContainer . children . length ; left ++ ) {
191- slot = tabContainer . children [ left ]
189+ slot = tabContainer . children [ left ] as TabElement
192190 if (
193191 left >= items . length ||
194192 ( slot . subject && ! slot . subject . sameTerm ( items [ left ] ) )
@@ -201,17 +199,14 @@ export function tabWidget (options) {
201199 return // The two just match in order: a case to optimize for
202200 }
203201 for ( right = tabContainer . children . length - 1 ; right >= 0 ; right -- ) {
204- slot = tabContainer . children [ right ]
202+ slot = tabContainer . children [ right ] as TabElement
205203 j = right - tabContainer . children . length + items . length
206204 if ( slot . subject && ! slot . subject . sameTerm ( items [ j ] ) ) {
207205 break
208206 }
209207 }
210208 // The elements left ... right in tabContainer.children do not match
211- const insertables = items . slice (
212- left ,
213- right - tabContainer . children . length + items . length + 1
214- )
209+ const insertables = items . slice ( left , right - tabContainer . children . length + items . length + 1 )
215210 while ( right >= left ) {
216211 // remove extra
217212 tabContainer . removeChild ( tabContainer . children [ left ] )
@@ -239,55 +234,61 @@ export function tabWidget (options) {
239234 }
240235 }
241236
242- const sync = function ( ) {
237+ function resetTabStyle ( ) {
238+ for ( let i = 0 ; i < tabContainer . children . length ; i ++ ) {
239+ const tab = tabContainer . children [ i ]
240+ if ( tab . classList . contains ( 'unstyled' ) ) {
241+ continue
242+ }
243+ if ( tab . children [ 0 ] ) {
244+ tab . children [ 0 ] . setAttribute ( 'style' , unselectedStyle )
245+ }
246+ }
247+ }
248+
249+ function resetBodyStyle ( ) {
250+ for ( let i = 0 ; i < bodyContainer . children . length ; i ++ ) {
251+ bodyContainer . children [ i ] . setAttribute ( 'style' , hiddenStyle )
252+ }
253+ }
254+
255+ function sync ( ) {
243256 if ( options . ordered ) {
244257 orderedSync ( )
245258 } else {
246259 // @@ SORT THE values
247260 orderedSync ( )
248261 }
249262 }
250- rootElement . refresh = sync
251- sync ( )
263+ }
252264
253- // From select-tabs branch by hand
254- if (
255- ! options . startEmpty &&
256- tabContainer . children . length &&
257- options . selectedTab
258- ) {
259- let tab
260- let found = false
261- for ( let i = 0 ; i < tabContainer . children . length ; i ++ ) {
262- tab = tabContainer . children [ i ]
263- if (
264- tab . firstChild &&
265- tab . firstChild . dataset . name === options . selectedTab
266- ) {
267- tab . firstChild . click ( )
268- found = true
269- }
270- }
271- if ( ! found ) {
272- tabContainer . children [ 0 ] . firstChild . click ( ) // Open first tab
273- }
274- } else if ( ! options . startEmpty && tabContainer . children . length ) {
275- tabContainer . children [ 0 ] . firstChild . click ( ) // Open first tab
265+ function getColors ( backgroundColor : string ) : [ string , string ] {
266+ return isLight ( backgroundColor )
267+ ? [ colorBlend ( backgroundColor , '#ffffff' , 0.3 ) , '#000000' ]
268+ : [ colorBlend ( backgroundColor , '#000000' , 0.3 ) , '#ffffff' ]
269+ }
270+
271+ function colorBlend ( a : string , b : string , mix : number ) : string {
272+ let ca , cb , res
273+ let str = '#'
274+ const hex = '0123456789abcdef'
275+ for ( let i = 0 ; i < 3 ; i ++ ) {
276+ ca = parseInt ( a . slice ( i * 2 + 1 , i * 2 + 3 ) , 16 )
277+ cb = parseInt ( b . slice ( i * 2 + 1 , i * 2 + 3 ) , 16 )
278+ res = ca * ( 1.0 - mix ) + cb * mix // @@@ rounding
279+ const res2 = parseInt ( ( '' + res ) . split ( '.' ) [ 0 ] ) // @@ ugh
280+ const h = parseInt ( ( '' + res2 / 16 ) . split ( '.' ) [ 0 ] ) // @@ ugh
281+ const l = parseInt ( ( '' + ( res2 % 16 ) ) . split ( '.' ) [ 0 ] ) // @@ ugh
282+ str += hex [ h ] + hex [ l ]
276283 }
277- return rootElement
284+ // console.log('Blending colors ' + a + ' with ' + mix + ' of ' + b + ' to give ' + str)
285+ return str
286+ }
278287
279- function addCancelButton ( tabContainer ) {
280- if ( tabContainer . dataset . onCloseSet ) {
281- // @@ TODO: this is only here to make the tests work
282- // Discussion at https://github.com/solid/solid-ui/pull/110#issuecomment-527080663
283- const existingCancelButton = tabContainer . querySelector ( '.unstyled' )
284- tabContainer . removeChild ( existingCancelButton )
285- }
286- const extraTab = dom . createElement ( tabElement )
287- extraTab . classList . add ( 'unstyled' )
288- const tabCancelButton = cancelButton ( dom , onClose )
289- extraTab . appendChild ( tabCancelButton )
290- tabContainer . appendChild ( extraTab )
291- tabContainer . dataset . onCloseSet = 'true'
288+ function isLight ( x : string ) : boolean {
289+ let total = 0
290+ for ( let i = 0 ; i < 3 ; i ++ ) {
291+ total += parseInt ( x . slice ( i * 2 + 1 , i * 2 + 3 ) , 16 )
292292 }
293+ return total > 128 * 3
293294}
0 commit comments