v1.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <?php
  2. namespace zin;
  3. require_once dirname(__DIR__) . DS . 'textarea' . DS . 'v1.php';
  4. class editor extends wg
  5. {
  6. protected static $version = '0.13.3'; // Keep in sync with ZenEditor release.
  7. /**
  8. * @var mixed[]
  9. */
  10. protected static $defineProps = array(
  11. 'uploadUrl?: string', // 图片上传链接
  12. 'placeholder?: string=""', // 占位文本
  13. 'fullscreenable?: bool=true', // 是否可全屏
  14. 'resizable?: bool=true', // 是否可拖拽调整大小
  15. 'exposeEditor?: bool=true', // 是否将编辑器实例挂载到 `window.$zenEditors`
  16. 'size?: string="sm"', // 尺寸,可选值 'sm', 'lg', 'full', 'auto'
  17. 'hideMenubar?: bool=false', // 是否隐藏菜单栏
  18. 'hideUI?: bool=false', // 是否隐藏整个编辑器 UI
  19. 'readonly?: bool=false', // 是否只读
  20. 'bubbleMenu?: bool=false', // 是否启用浮动菜单
  21. 'slashMenu?: bool=false', // 是否启用 `/` 菜单
  22. 'menubarMode?: string="compact"', // 菜单栏模式,可选值 'basic', 'compact', 'full'
  23. 'locale?: string', // 语言,可选值 'zh', 'en',默认跟随浏览器,也可以是自定义的语言项 JSON,详见 ZenEditor 文档
  24. 'markdown?: bool=false', // 是否启用 Markdown 模式,若启用,为了兼容性将会隐藏一些功能
  25. 'neglectDefaultTextStyle?: bool=false', // 是否不赋予默认的文本样式
  26. 'preferHardBreak?: bool=false', // 是否优先使用硬回车而不是新段落
  27. 'value?: string', // 初始内容
  28. 'templateType?: string', // 模板类型
  29. 'uid?: string' // 图片上传 uid
  30. );
  31. /**
  32. * @var string
  33. */
  34. protected static $css = <<<CSS
  35. .editor {border: unset; border-radius: unset; color: var(--color-fore)}
  36. .editor.size-auto {min-height: 0;}
  37. zen-editor-menu-item {display: inline-flex; align-items: center;}
  38. zen-editor-menu-item > .menu-item {color: #64758B!important; display: inline-flex; align-items: center; justify-content: center;}
  39. zen-editor-menu-item > .menu-item:hover {color: var(--color-primary-400)!important; background-color: var(--color-gray-100)!important;}
  40. zen-editor-menu-item > .menu-item.is-active {color: var(--color-primary-400)!important; background-color: transparent!important; box-shadow: inset 0 0 0 1px var(--color-primary-300);}
  41. zen-editor-menu-item > .menu-item.is-active:hover {background-color: var(--color-gray-200)!important;}
  42. zen-editor-menu-item > .menu-item.is-active:not(:hover) {background-color: transparent!important;}
  43. zen-editor-menu-item > .menu-item:has(.color):hover, zen-editor-menu-item > .menu-item:has(.color).is-active {background-color: ransparent!important; box-shadow: inset 0 0 0 1px var(--color-primary-300)!important;}
  44. .menubar {border-bottom: 1px solid #d8dbde!important; padding: 0.125rem;}
  45. .tippy-content > div {border: 1px solid #d8dbde!important;}
  46. .tippy-content zen-editor-menu-item {line-height: normal;}
  47. .tippy-content zen-editor-menu-item .label {all: unset;}
  48. .tiptap.ProseMirror {padding-top: 0.5rem;}
  49. .tiptap.ProseMirror p {margin: 0;}
  50. CSS;
  51. public static function getPageCSS()
  52. {
  53. return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
  54. }
  55. public static function getPageJS()
  56. {
  57. $version = self::$version;
  58. $content = file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
  59. $content .= "$.getLib('zen-editor/zen-editor.esm.js?v=$version', {type: 'module'}, () => {document.body.dataset.loadedEditor = true;});";
  60. return $content;
  61. }
  62. protected function created()
  63. {
  64. if(empty($this->prop('uid'))) $this->setProp('uid', uniqid());
  65. $this->setDefaultProps(array('uploadUrl' => createLink('file', 'ajaxUpload', 'uid=' . $this->prop('uid'))));
  66. if(helper::getBrowser()['name'] == 'safari') $this->setProp('neglectDefaultTextStyle', true);
  67. }
  68. /**
  69. * @param string $editor
  70. * @param string $type
  71. */
  72. protected function buildTemplate($editor, $type)
  73. {
  74. global $app, $lang;
  75. $app->loadLang('user');
  76. jsVar('templateEmpty', $lang->user->tplContentNotEmpty);
  77. jsVar('confirmDeleteTemplate', $lang->user->confirmDeleteTemplate);
  78. return btnGroup
  79. (
  80. setClass('absolute right-0'),
  81. isInModal() ? null : btn
  82. (
  83. setClass('ghost border-l border-r border-light'),
  84. on::click("window.showSaveModal('$editor', '$type')"),
  85. $lang->user->saveTemplate
  86. ),
  87. dropdown
  88. (
  89. btn($lang->user->applyTemplate, setClass('ghost')),
  90. set::items(array('url' => createLink('user', 'ajaxGetTemplates', "editor=$editor&type=$type")))
  91. )
  92. );
  93. }
  94. protected function build()
  95. {
  96. global $lang, $app;
  97. $editor = new h
  98. (
  99. setTag('zen-editor'),
  100. setClass('p-0', $this->prop('readonly') ? '' : 'form-control'),
  101. $this->prop('size') === 'full' ? setStyle('height', '100%') : setClass('h-auto')
  102. );
  103. $props = $this->props->pick(array('uploadUrl', 'placeholder', 'fullscreenable', 'resizable', 'exposeEditor', 'size', 'hideMenubar', 'hideUI', 'readonly', 'bubbleMenu', 'slashMenu', 'menubarMode', 'locale', 'markdown', 'neglectDefaultTextStyle', 'preferHardBreak'));
  104. foreach($props as $key => $value)
  105. {
  106. if($key == 'placeholder' && empty($value)) $value = $lang->noticePasteImg;
  107. if($value === true || (is_string($value) && !empty($value))) $editor->add(set(uncamelize($key), $value));
  108. }
  109. $customProps = $this->getRestProps();
  110. if(!isset($customProps['class'])) $customProps['class'] = 'w-full';
  111. $addCss = '';
  112. if($this->prop('maxHeight'))
  113. {
  114. $height = $this->prop('maxHeight');
  115. $addCss = <<<CSS
  116. .editor {max-height: {$height};}
  117. CSS;
  118. }
  119. $editor->setProp($customProps);
  120. $editor->setProp('css', static::$css . $addCss); // Inject CSS into editor.
  121. $editor->setProp('css-src', $app->getWebRoot() . 'js/zui3/zen-editor/zui-inject-style.css'); // Inject CSS on page, for tippy menus.
  122. /* Set initial content. */
  123. $initialContent = $this->prop('value');
  124. $children = $this->children();
  125. if(empty($children)) $editor->setProp('initial-content', $initialContent);
  126. else $editor->add(h('article', set('slot', 'content'), $initialContent? html($initialContent) : null, $children));
  127. $templateType = $this->prop('templateType');
  128. return div
  129. (
  130. setClass('editor-container p-px mt-px rounded relative', $this->prop('readonly') ? 'is-readonly' : ''),
  131. $props['size'] === 'full' ? setStyle('height', '100%') : setClass('h-auto'),
  132. h::css(static::$css . $addCss), // Inject CSS on page, for tippy menus.
  133. $templateType ? $this->buildTemplate($this->prop('name'), $templateType) : null,
  134. $editor,
  135. textarea
  136. (
  137. $this->prop('value'),
  138. set::rows(1),
  139. set::size($props['size'])
  140. ),
  141. input
  142. (
  143. set::name('uid'),
  144. set::value($this->prop('uid')),
  145. setClass('hidden')
  146. )
  147. );
  148. }
  149. }