Content.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. <?php
  2. namespace Dcat\Admin\Layout;
  3. use Closure;
  4. use Dcat\Admin\Admin;
  5. use Dcat\Admin\Traits\HasBuilderEvents;
  6. use Illuminate\Contracts\Support\Renderable;
  7. use Illuminate\Support\ViewErrorBag;
  8. class Content implements Renderable
  9. {
  10. use HasBuilderEvents;
  11. /**
  12. * @var string
  13. */
  14. protected $view = 'admin::layouts.content';
  15. /**
  16. * @var array
  17. */
  18. protected $variables = [];
  19. /**
  20. * Content title.
  21. *
  22. * @var string
  23. */
  24. protected $title = '';
  25. /**
  26. * Content description.
  27. *
  28. * @var string
  29. */
  30. protected $description = '';
  31. /**
  32. * Page breadcrumb.
  33. *
  34. * @var array
  35. */
  36. protected $breadcrumb = [];
  37. /**
  38. * @var Row[]
  39. */
  40. protected $rows = [];
  41. /**
  42. * @var array
  43. */
  44. protected $config = [];
  45. /**
  46. * Content constructor.
  47. *
  48. * @param Closure|null $callback
  49. */
  50. public function __construct(\Closure $callback = null)
  51. {
  52. $this->callResolving();
  53. if ($callback instanceof Closure) {
  54. $callback($this);
  55. }
  56. }
  57. /**
  58. * Create a content instance.
  59. *
  60. * @param mixed ...$params
  61. *
  62. * @return $this
  63. */
  64. public static function make(...$params)
  65. {
  66. return new static(...$params);
  67. }
  68. /**
  69. * @param string $header
  70. *
  71. * @return $this
  72. */
  73. public function header($header = '')
  74. {
  75. return $this->title($header);
  76. }
  77. /**
  78. * Set title of content.
  79. *
  80. * @param string $title
  81. *
  82. * @return $this
  83. */
  84. public function title($title)
  85. {
  86. $this->title = $title;
  87. return $this;
  88. }
  89. /**
  90. * Set description of content.
  91. *
  92. * @param string $description
  93. *
  94. * @return $this
  95. */
  96. public function description($description = '')
  97. {
  98. $this->description = $description;
  99. return $this;
  100. }
  101. /**
  102. * @return $this
  103. */
  104. public function full()
  105. {
  106. $this->view = 'admin::layouts.full-content';
  107. Admin::asset()->full();
  108. return $this->withConfig('blank_page', true);
  109. }
  110. /**
  111. * Set breadcrumb of content.
  112. *
  113. * @example
  114. * $this->breadcrumb('Menu', 'auth/menu', 'fa fa-align-justify');
  115. * $this->breadcrumb([
  116. * ['text' => 'Menu', 'url' => 'auth/menu', 'icon' => 'fa fa-align-justify']
  117. * ]);
  118. *
  119. * @param array ...$breadcrumb
  120. *
  121. * @return $this
  122. */
  123. public function breadcrumb(...$breadcrumb)
  124. {
  125. $this->formatBreadcrumb($breadcrumb);
  126. $this->breadcrumb = array_merge($this->breadcrumb, $breadcrumb);
  127. return $this;
  128. }
  129. /**
  130. * @param array $breadcrumb
  131. *
  132. * @throws \Exception
  133. *
  134. * @return void
  135. */
  136. protected function formatBreadcrumb(array &$breadcrumb)
  137. {
  138. if (! $breadcrumb) {
  139. throw new \Exception('Breadcrumb format error!');
  140. }
  141. $notArray = false;
  142. foreach ($breadcrumb as &$item) {
  143. $isArray = is_array($item);
  144. if ($isArray && ! isset($item['text'])) {
  145. throw new \Exception('Breadcrumb format error!');
  146. }
  147. if (! $isArray && $item) {
  148. $notArray = true;
  149. }
  150. }
  151. if (! $breadcrumb) {
  152. throw new \Exception('Breadcrumb format error!');
  153. }
  154. if ($notArray) {
  155. $breadcrumb = [
  156. [
  157. 'text' => $breadcrumb[0] ?? null,
  158. 'url' => $breadcrumb[1] ?? null,
  159. 'icon' => $breadcrumb[2] ?? null,
  160. ],
  161. ];
  162. }
  163. }
  164. /**
  165. * Alias of method row.
  166. *
  167. * @param mixed $content
  168. *
  169. * @return Content
  170. */
  171. public function body($content)
  172. {
  173. return $this->row($content);
  174. }
  175. /**
  176. * Add one row for content body.
  177. *
  178. * @param $content
  179. *
  180. * @return $this
  181. */
  182. public function row($content)
  183. {
  184. if ($content instanceof Closure) {
  185. $row = new Row();
  186. call_user_func($content, $row);
  187. $this->addRow($row);
  188. } else {
  189. $this->addRow(new Row($content));
  190. }
  191. return $this;
  192. }
  193. /**
  194. * @param $content
  195. *
  196. * @return $this
  197. */
  198. public function prepend($content)
  199. {
  200. if ($content instanceof Closure) {
  201. $row = new Row();
  202. call_user_func($content, $row);
  203. $this->prependRow($row);
  204. } else {
  205. $this->prependRow(new Row($content));
  206. }
  207. return $this;
  208. }
  209. protected function prependRow(Row $row)
  210. {
  211. array_unshift($this->rows, $row);
  212. }
  213. /**
  214. * Add Row.
  215. *
  216. * @param Row $row
  217. */
  218. protected function addRow(Row $row)
  219. {
  220. $this->rows[] = $row;
  221. }
  222. /**
  223. * Build html of content.
  224. *
  225. * @return string
  226. */
  227. public function build()
  228. {
  229. $html = '';
  230. foreach ($this->rows as $row) {
  231. $html .= $row->render();
  232. }
  233. return $html;
  234. }
  235. /**
  236. * Set success message for content.
  237. *
  238. * @param string $title
  239. * @param string $message
  240. *
  241. * @return $this
  242. */
  243. public function withSuccess($title = '', $message = '')
  244. {
  245. admin_success($title, $message);
  246. return $this;
  247. }
  248. /**
  249. * Set error message for content.
  250. *
  251. * @param string $title
  252. * @param string $message
  253. *
  254. * @return $this
  255. */
  256. public function withError($title = '', $message = '')
  257. {
  258. admin_error($title, $message);
  259. return $this;
  260. }
  261. /**
  262. * Set warning message for content.
  263. *
  264. * @param string $title
  265. * @param string $message
  266. *
  267. * @return $this
  268. */
  269. public function withWarning($title = '', $message = '')
  270. {
  271. admin_warning($title, $message);
  272. return $this;
  273. }
  274. /**
  275. * Set info message for content.
  276. *
  277. * @param string $title
  278. * @param string $message
  279. *
  280. * @return $this
  281. */
  282. public function withInfo($title = '', $message = '')
  283. {
  284. admin_info($title, $message);
  285. return $this;
  286. }
  287. /**
  288. * Set content view.
  289. *
  290. * @param null|string $view
  291. *
  292. * @return $this
  293. */
  294. public function view(?string $view)
  295. {
  296. $this->view = $view;
  297. return $this;
  298. }
  299. /**
  300. * @param string|array $key
  301. * @param mixed $value
  302. *
  303. * @return $this
  304. */
  305. public function with($key, $value = null)
  306. {
  307. if (is_array($key)) {
  308. $this->variables = array_merge($this->variables, $key);
  309. } else {
  310. $this->variables[$key] = $value;
  311. }
  312. return $this;
  313. }
  314. /**
  315. * @param string|array $key
  316. * @param mixed $value
  317. *
  318. * @return $this
  319. */
  320. public function withConfig($key, $value = null)
  321. {
  322. if (is_array($key)) {
  323. $this->config = array_merge($this->config, $key);
  324. } else {
  325. $this->config[$key] = $value;
  326. }
  327. return $this;
  328. }
  329. /**
  330. * @return void
  331. */
  332. protected function shareDefaultErrors()
  333. {
  334. if (! session()->all()) {
  335. view()->share(['errors' => new ViewErrorBag()]);
  336. }
  337. }
  338. /**
  339. * @return array
  340. */
  341. protected function variables()
  342. {
  343. return array_merge([
  344. 'header' => $this->title,
  345. 'description' => $this->description,
  346. 'breadcrumb' => $this->breadcrumb,
  347. 'configData' => $this->applClasses(),
  348. 'content' => $this->build(),
  349. 'pjaxContainerId' => Admin::$pjaxContainerId,
  350. ], $this->variables);
  351. }
  352. /**
  353. * @return array
  354. */
  355. protected function applClasses()
  356. {
  357. // default data array
  358. $defaultData = [
  359. 'main_layout_type' => 'vertical',
  360. 'theme' => 'light',
  361. 'sidebar_collapsed' => false,
  362. 'navbar_color' => '',
  363. 'horizontal_menu_type' => 'floating',
  364. 'vertical_menu_navbar_type' => 'floating',
  365. 'footer_type' => 'static', //footer
  366. 'body_class' => '',
  367. 'content_layout' => 'default',
  368. 'blank_page' => false,
  369. 'direction' => env('MIX_CONTENT_DIRECTION', 'ltr'),
  370. ];
  371. $data = array_merge(
  372. config('admin.layout') ?: [],
  373. $this->config
  374. );
  375. // All options available in the template
  376. $allOptions = [
  377. 'main_layout_type' => ['vertical', 'horizontal'],
  378. 'theme' => ['light' => 'light', 'dark' => 'dark-layout', 'semi-dark' => 'semi-dark-layout'],
  379. 'sidebar_collapsed' => [true, false],
  380. 'navbar_color' => ['bg-primary', 'bg-info', 'bg-warning', 'bg-success', 'bg-danger', 'bg-dark'],
  381. 'content_layout' => ['default', 'content-left-sidebar', 'content-right-sidebar', 'content-detached-left-sidebar', 'content-detached-right-sidebar'],
  382. 'sidebar_position_class' => ['content-left-sidebar' => 'sidebar-left', 'content-right-sidebar' => 'sidebar-right', 'content-detached-left-sidebar' => 'sidebar-detached sidebar-left', 'content-detached-right-sidebar' => 'sidebar-detached sidebar-right', 'default' => 'default-sidebar-position'],
  383. 'content_sidebar_class' => ['content-left-sidebar' => 'content-right', 'content-right-sidebar' => 'content-left', 'content-detached-left-sidebar' => 'content-detached content-right', 'content-detached-right-sidebar' => 'content-detached content-left', 'default' => 'default-sidebar'],
  384. 'direction' => ['ltr', 'rtl'],
  385. 'horizontal_menu_type' => ['floating' => 'navbar-floating', 'static' => 'navbar-static', 'sticky' => 'navbar-sticky'],
  386. 'horizontal_menu_class' => ['static' => 'menu-static', 'sticky' => 'fixed-top', 'floating' => 'floating-nav'],
  387. 'vertical_menu_navbar_type' => ['floating' => 'navbar-floating', 'static' => 'navbar-static', 'sticky' => 'navbar-sticky', 'hidden' => 'navbar-hidden'],
  388. 'navbar_class' => ['floating' => 'floating-nav', 'static' => 'static-top', 'sticky' => 'fixed-top', 'hidden' => 'd-none'],
  389. 'footer_type' => ['static' => 'footer-static', 'sticky' => 'fixed-footer', 'hidden' => 'footer-hidden'],
  390. ];
  391. $maps = [
  392. 'content_layout' => 'sidebar_position_class',
  393. 'horizontal_menu_type' => 'horizontal_menu_type',
  394. 'vertical_menu_navbar_type' => 'vertical_menu_navbar_type',
  395. 'footer_type' => 'footer_type',
  396. ];
  397. foreach ($allOptions as $key => $value) {
  398. if (! array_key_exists($key, $defaultData)) {
  399. continue;
  400. }
  401. if (! isset($data[$key])) {
  402. $data[$key] = $defaultData[$key];
  403. continue;
  404. }
  405. if (
  406. isset($maps[$key])
  407. && ! isset($allOptions[$maps[$key]][$data[$key]])
  408. ) {
  409. $data[$key] = $defaultData[$key];
  410. }
  411. }
  412. // layout classes
  413. return [
  414. 'theme' => $data['theme'],
  415. 'layout_theme' => $allOptions['theme'][$data['theme']] ?? $data['theme'],
  416. 'sidebar_collapsed' => $data['sidebar_collapsed'],
  417. 'vertical_menu_navbar_type' => $allOptions['vertical_menu_navbar_type'][$data['vertical_menu_navbar_type']],
  418. 'navbar_class' => $allOptions['navbar_class'][$data['vertical_menu_navbar_type']],
  419. 'navbar_color' => $data['navbar_color'],
  420. 'horizontal_menu_type' => $allOptions['horizontal_menu_type'][$data['horizontal_menu_type']],
  421. 'horizontal_menu_class' => $allOptions['horizontal_menu_class'][$data['horizontal_menu_type']],
  422. 'footer_type' => $allOptions['footer_type'][$data['footer_type']],
  423. 'sidebar_class' => $data['sidebar_collapsed'] ? 'menu-collapsed' : 'menu-expanded',
  424. 'body_class' => $data['body_class'],
  425. 'blank_page' => $data['blank_page'],
  426. 'blank_page_class' => $data['blank_page'] ? 'blank-page' : '',
  427. 'content_layout' => $data['content_layout'],
  428. 'sidebar_position_class' => $allOptions['sidebar_position_class'][$data['content_layout']],
  429. 'content_sidebar_class' => $allOptions['content_sidebar_class'][$data['content_layout']],
  430. 'main_layout_type' => $data['main_layout_type'],
  431. 'direction' => $data['direction'],
  432. ];
  433. }
  434. /**
  435. * Render this content.
  436. *
  437. * @return string
  438. */
  439. public function render()
  440. {
  441. $this->callComposing();
  442. $this->shareDefaultErrors();
  443. $variables = $this->variables();
  444. $this->callComposed();
  445. return view($this->view, $variables)->render();
  446. }
  447. /**
  448. * Register a composed event.
  449. *
  450. * @param callable $callback
  451. * @param bool $once
  452. */
  453. public static function composed(callable $callback, bool $once = false)
  454. {
  455. static::addBuilderListeners('builder.composed', $callback, $once);
  456. }
  457. /**
  458. * Call the composed callbacks.
  459. *
  460. * @param array ...$params
  461. */
  462. protected function callComposed(...$params)
  463. {
  464. $this->fireBuilderEvent('builder.composed', ...$params);
  465. }
  466. }