@ -6,7 +6,7 @@ import { theme } from "app/client/ui2018/cssVars";
import { icon } from "app/client/ui2018/icons" ;
import { hoverTooltip , overflowTooltip } from 'app/client/ui/tooltips' ;
import { menu , menuItem , menuText } from "app/client/ui2018/menus" ;
import { dom, domComputed , DomElementArg , makeTestId , observable , Observable , styled } from "grainjs" ;
import { Computed, dom, domComputed , DomElementArg , makeTestId , observable , Observable , styled } from "grainjs" ;
const t = makeT ( 'pages' ) ;
@ -54,17 +54,21 @@ export function buildPageDom(name: Observable<string>, actions: PageActions, ...
}
} ) ;
const splitName = Computed . create ( null , name , ( use , _name ) = > splitPageInitial ( _name ) ) ;
return pageElem = dom (
'div' ,
dom . autoDispose ( lis ) ,
dom . autoDispose ( splitName ) ,
domComputed ( ( use ) = > use ( name ) === '' , blank = > blank ? dom ( 'div' , '-' ) :
domComputed ( isRenaming , ( isrenaming ) = > (
isrenaming ?
cssPageItem (
cssPageInitial (
testId ( 'initial' ) ,
dom . text ( ( use ) = > Array . from ( use ( name ) ) [ 0 ] )
) ,
dom . text ( ( use ) = > use ( splitName ) . initial ) ,
cssPageInitial . cls ( '-emoji' , ( use ) = > use ( splitName ) . hasEmoji ) ,
) ,
cssEditorInput (
{
initialValue : name.get ( ) || '' ,
@ -82,10 +86,11 @@ export function buildPageDom(name: Observable<string>, actions: PageActions, ...
cssPageItem (
cssPageInitial (
testId ( 'initial' ) ,
dom . text ( ( use ) = > Array . from ( use ( name ) ) [ 0 ] ) ,
dom . text ( ( use ) = > use ( splitName ) . initial ) ,
cssPageInitial . cls ( '-emoji' , ( use ) = > use ( splitName ) . hasEmoji ) ,
) ,
cssPageName (
dom . text ( n ame) ,
dom . text ( ( use ) = > use ( splitName ) . displayN ame) ,
testId ( 'label' ) ,
dom . on ( 'click' , ( ev ) = > isTargetSelected ( ev . target as HTMLElement ) && isRenaming . set ( true ) ) ,
overflowTooltip ( ) ,
@ -122,6 +127,24 @@ export function buildCensoredPage() {
) ;
}
// This crazy expression matches all "possible emoji" and comes from a very official source:
// https://unicode.org/reports/tr51/#EBNF_and_Regex (linked from
// https://stackoverflow.com/a/68146409/328565). It is processed from the original by replacing \x
// with \u, removing whitespace, and factoring out a long subexpression.
const emojiPart = / ( ? : \ p { R I } \ p { R I } | \ p { E m o j i } ( ? : \ p { E M o d } | \ u { F E 0 F } \ u { 2 0 E 3 } ? | [ \ u { E 0 0 2 0 } - \ u { E 0 0 7 E } ] + \ u { E 0 0 7 F } ) ? ) / u ;
const pageInitialRegex = new RegExp ( ` ^ ${ emojiPart . source } (?: \\ u{200D} ${ emojiPart . source } )* ` , "u" ) ;
// Divide up the page name into an "initial" and "displayName", where an emoji initial, if
// present, is omitted from the displayName, but a regular character used as the initial is kept.
function splitPageInitial ( name : string ) : { initial : string , displayName : string , hasEmoji : boolean } {
const m = name . match ( pageInitialRegex ) ;
if ( m ) {
return { initial : m [ 0 ] , displayName : name.slice ( m [ 0 ] . length ) . trim ( ) , hasEmoji : true } ;
} else {
return { initial : Array.from ( name ) [ 0 ] , displayName : name.trim ( ) , hasEmoji : false } ;
}
}
const cssPageItem = styled ( 'a' , `
display : flex ;
flex - direction : row ;
@ -129,7 +152,8 @@ const cssPageItem = styled('a', `
align - items : center ;
flex - grow : 1 ;
. $ { treeViewContainer . className } - close & {
margin - left : 16px ;
display : flex ;
justify - content : center ;
}
& , & : hover , & : focus {
text - decoration : none ;
@ -143,10 +167,25 @@ const cssPageInitial = styled('div', `
color : $ { theme . pageInitialsFg } ;
border - radius : 3px ;
background - color : $ { theme . pageInitialsBg } ;
width : 16px ;
height : 16px ;
text - align : center ;
width : 20px ;
height : 20px ;
margin - right : 8px ;
display : flex ;
justify - content : center ;
align - items : center ;
& - emoji {
background - color : $ { theme . pageInitialsEmojiBg } ;
box - shadow : 0 0 0 1 px var ( -- grist - theme - left - panel - page - emoji - outline , var ( -- grist - color - dark - grey ) ) ;
font - size : 15px ;
overflow : hidden ;
}
. $ { treeViewContainer . className } - close & {
margin - right : 0 ;
}
. $ { itemHeader . className } . selected & - emoji {
box - shadow : none ;
}
` );
const cssPageName = styled ( 'div' , `