Skip to content

Commit

Permalink
Vue layout/pages bootstrap, wp.menus and other props #minor
Browse files Browse the repository at this point in the history
  • Loading branch information
craigrileyuk committed Dec 20, 2024
1 parent b3f3301 commit dd22b8b
Show file tree
Hide file tree
Showing 16 changed files with 300 additions and 6 deletions.
4 changes: 4 additions & 0 deletions changelog/next.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- **Feature**: Menus by location now available on the `wp.menus` page prop
- **Feature**: Filter hooks added, more information coming soon
- **Improvement**: Added default layout and pages to bootstrapped Vue theme
- **Improvement**: `logo`, `name` and `homeUrl` also now available on `wp` page prop
33 changes: 31 additions & 2 deletions src/Commands/CreateThemeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,18 @@ public function __invoke($args, $assocArgs)

// MAIN JS
wp_mkdir_p(Path::join($themeDir, 'resources', 'js'));
wp_mkdir_p(Path::join($themeDir, 'resources', 'js', 'pages'));

$this->copyStub(Path::join($stubsPath, 'resources', 'js', 'main.js.' . $template . '.stub'), Path::join($themeDir, 'resources', 'js', 'main.js'));
$this->copyStub(Path::join($stubsPath, 'resources', 'js', 'ssr.js.' . $template . '.stub'), Path::join($themeDir, 'resources', 'js', 'ssr.js'));

// TEMPLATE FILES
if ($template === "vue") $this->copyVueTemplateFiles($stubsPath, $themeDir);

// THEME CSS
wp_mkdir_p(Path::join($themeDir, 'resources', 'css'));
$this->copyStub(Path::join($stubsPath, 'resources', 'css', 'style.postcss.stub'), Path::join($themeDir, 'resources', 'css', 'style.postcss'));


// COMPOSER INSTALL
$process = new Process(['composer', 'install'], $themeDir);
$process->setTimeout(300);
Expand All @@ -120,6 +127,26 @@ public function __invoke($args, $assocArgs)
return true;
}

private function copyVueTemplateFiles($stubsPath, $themeDir)
{
wp_mkdir_p(Path::join($themeDir, 'resources', 'js', 'pages'));
wp_mkdir_p(Path::join($themeDir, 'resources', 'js', 'layouts'));

$pages = glob(Path::join($stubsPath, 'resources', 'js', 'pages', '*.vue.stub'));
foreach ($pages as $page) {
$filename = basename($page, '.stub'); // Remove the .stub extension
$targetFile = Path::join($themeDir, 'resources', 'js', 'pages', $filename);
$this->copyStub($page, $targetFile);
}

$layouts = glob(Path::join($stubsPath, 'resources', 'js', 'layouts', '*.vue.stub'));
foreach ($layouts as $layout) {
$filename = basename($layout, '.stub'); // Remove the .stub extension
$targetFile = Path::join($themeDir, 'resources', 'js', 'layouts', $filename);
$this->copyStub($layout, $targetFile);
}
}

private function applyReplacements(string $content): string
{
foreach ($this->replacements as $search => $replace) {
Expand All @@ -132,6 +159,8 @@ private function copyStub(string $from, string $to): void
{
$content = file_get_contents($from);
$content = $this->applyReplacements($content);
file_put_contents($to, $content);
if (!file_put_contents($to, $content)) {
throw new \RuntimeException("Failed to write $from to location: $to");
};
}
}
10 changes: 10 additions & 0 deletions src/Helpers/HookFilters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace EvoMark\InertiaWordpress\Helpers;

class HookFilters
{
public const SHARE_MENU = "inertia_wordpress_share_menu";
public const SHARE_MENU_ITEMS_ARGS = "inertia_wordpress_share_menu_items_args";
public const MENU_ITEM = "inertia_wordpress_menu_item";
}
94 changes: 94 additions & 0 deletions src/Helpers/Wordpress.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use EvoMark\InertiaWordpress\Resources\ImageResource;
use EvoMark\InertiaWordpress\Resources\PostSimpleResource;
use EvoMark\InertiaWordpress\Resources\ArchivePaginationResource;
use EvoMark\InertiaWordpress\Resources\MenuItemResource;
use stdClass;

class Wordpress
{
Expand Down Expand Up @@ -68,6 +70,98 @@ public static function getGlobalPost()
return $post;
}

public static function getCustomLogo(): ?stdClass
{
if (! has_custom_logo()) return null;
$logoId = get_theme_mod('custom_logo');
return ImageResource::single($logoId);
}

public static function getNavigationMenus(): array
{
$menus = [];
$registered = get_registered_nav_menus();
$locations = get_nav_menu_locations();
foreach ($locations as $name => $menuId) {
$menus[$name] = [
'label' => $registered[$name],
...self::getNavigationMenu($menuId)
];
}
return $menus;
}

public static function getNavigationMenu($menuId): array
{
$menuObject = wp_get_nav_menu_object($menuId);

/**
* Filter the arguments supplied to each call to `wp_get_nav_menu_items` when generating menus
*
* @since 0.2.0
*
* @param array $args The args to pass
* @param \WP_Term $menuObject The menu term object
* @return array $args
*/
$args = apply_filters(HookFilters::SHARE_MENU_ITEMS_ARGS, [], $menuObject);

$data = [
'menuId' => $menuObject->term_id,
'menuName' => $menuObject->name,
'menuSlug' => $menuObject->slug,
'menuCount' => $menuObject->count,
'menuDescription' => $menuObject->description,
'items' => self::createMenuTree($menuId, $args)
];

/**
* Called before each compiled menu object is passed to the frontend
*
* @since 0.2.0
*
* @param array $data The compiled menu data object
* @param \WP_Term $menuObject The raw Wordpress term object for the menu
* @param int $menuId The term ID of the menu
* @return array $data
*/
return apply_filters(HookFilters::SHARE_MENU, $data, $menuObject, $menuId);
}

public static function createMenuTree($menuId, $args): array
{
$items = wp_get_nav_menu_items($menuId, $args);
$items_by_parent = [];
foreach ($items as $item) {
$items_by_parent[$item->menu_item_parent][] = $item;
}

$buildTree = function ($parentId) use (&$items_by_parent, &$buildTree) {
$tree = [];
if (isset($items_by_parent[$parentId])) {
foreach ($items_by_parent[$parentId] as $item) {
$item = MenuItemResource::single($item);
$children = $buildTree($item->id);
$item->items = count($children) > 0 ? $children : null;

/**
* Called when adding a menu item
*
* @since 0.2.0
*
* @param stdClass $item The generated menu item object
* @param string $parentId The ID of the menu item's parent. Default is "0"
* @return stdClass $item
*/
$tree[] = apply_filters(HookFilters::MENU_ITEM, $item, $parentId);
}
}
return $tree;
};

return $buildTree("0");
}

public static function getAdminBar()
{
/* require_once ABSPATH . WPINC . '/class-wp-admin-bar.php';
Expand Down
3 changes: 3 additions & 0 deletions src/Inertia.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ public static function getArchive(): Archive
return Wordpress::getArchiveData();
}

/**
* Get a processed post object, defaults to global
*/
public static function getPost(?\WP_Post $post = null, array $args = null)
{
if (empty($post)) {
Expand Down
6 changes: 5 additions & 1 deletion src/Request/RequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,14 @@ public function setGlobalShares()
{
$this->share('errors', Inertia::always(RequestResponse::getFormattedErrors()));
$this->share('wp', [
'name' => get_bloginfo('name'),
'adminBar' => $this->isInertia() ? fn() => Wordpress::getAdminBar() : null,
'restUrl' => get_rest_url(),
'user' => is_user_logged_in() ? UserResource::single(wp_get_current_user()) : null,
'userCapabilities' => is_user_logged_in() ? Wordpress::getUserCapabilities(wp_get_current_user()) : null
'userCapabilities' => is_user_logged_in() ? Wordpress::getUserCapabilities(wp_get_current_user()) : null,
'logo' => Wordpress::getCustomLogo(),
'homeUrl' => home_url(),
'menus' => Wordpress::getNavigationMenus()
]);
}
}
35 changes: 35 additions & 0 deletions src/Resources/MenuItemResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace EvoMark\InertiaWordpress\Resources;

use EvoMark\InertiaWordpress\Contracts\InertiaResource;
use stdClass;

class MenuItemResource implements InertiaResource
{
public static function collection(?array $menus): array
{
if (empty($menus) || !$menus) return [];
else return array_map(fn($m) => self::single($m), $menus);
}

public static function single($menu): stdClass
{
if (is_integer($menu)) {
$menu = get_post($menu);
}

return (object) [
'id' => $menu->ID,
'parent' => $menu->menu_item_parent,
'label' => $menu->title,
'slug' => $menu->post_name,
'type' => $menu->type,
'typeLabel' => $menu->type_label,
'url' => $menu->url,
'target' => $menu->target,
'rel' => $menu->xfn,
'classes' => esc_attr($menu->classes ? implode(' ', $menu->classes) : '')
];
}
}
13 changes: 12 additions & 1 deletion src/Theme/ThemeSetup.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ public static function init()
self::addTemplateDirectories();
self::enqueueScripts();
self::getThemeVersion();
self::addThemeSupport();
}

public static function addThemeSupport()
{
add_theme_support('custom-logo');
add_theme_support('post-thumbnails');
add_theme_support('title-tag');
}

public static function enqueueScripts()
Expand All @@ -36,11 +44,14 @@ public static function getThemeVersion()
$viteDir = Path::join(wp_upload_dir()['basedir'], 'scw-vite-hmr', $entryNamespace);
$container = Container::getInstance();
$request = $container->get('requestHandler');
$manifestPath = Path::join($viteDir, 'build', 'manifest.json');

if (file_exists(Path::join($viteDir, 'hot'))) {
$request->setVersion("dev");
} else if (file_exists($manifestPath)) {
$request->setVersion(md5_file($manifestPath));
} else {
$request->setVersion(md5_file(Path::join($viteDir, 'build', 'manifest.json')));
$request->setVersion("unknown");
}
}

Expand Down
3 changes: 3 additions & 0 deletions stubs/theme/package.json.vue.stub
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@
"vite": "^6.0.3",
"vue": "^3.5.13",
"wordpress-vite-plugin": "^1.1.0"
},
"devDependencies": {
"@vue/server-renderer": "^3.5.13"
}
}
9 changes: 9 additions & 0 deletions stubs/theme/resources/css/style.postcss.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#app {
display: flex;
flex-direction: column;
min-height: calc(100vh - var(--wp-admin--admin-bar--height));
}

main {
flex-grow: 1;
}
72 changes: 72 additions & 0 deletions stubs/theme/resources/js/layouts/DefaultLayout.vue.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<template>
<header>
<div>
<Link :href="homeUrl">
<img
v-if="logo"
:src="logo.sizes.large.url"
:alt="logo.metadata.alt"
/>
<p v-else>{{ siteName }}</p>
</Link>
</div>
<nav>
<ul v-if="headerMenu" id="header-menu">
<li v-for="item in headerMenu.items">
<a
v-if="item.type === 'custom'"
:href="item.url"
target="_blank"
>{{ item.label }}</a
>
<Link v-else :href="item.url">{{ item.label }}</Link>
</li>
</ul>
</nav>
</header>
<slot></slot>
<footer>
<div>&copy; 2024 {{ siteName }}</div>
</footer>
</template>

<script setup>
import { usePage, Link } from "@inertiajs/vue3";
import { computed } from "vue";

const logo = computed(() => usePage().props.wp.logo);
const homeUrl = computed(() => usePage().props.wp.homeUrl);
const siteName = computed(() => usePage().props.wp.name);
const headerMenu = computed(
() => usePage().props.wp.menus["header-menu"] ?? null
);
</script>

<style lang="postcss" scoped>
header,
footer {
display: flex;
justify-content: space-between;
align-items: center;
background-color: hsl(204, 15%, 13%);
color: #ffffff;
padding: 1rem 2rem;
}

footer {
margin-top: 2rem;
}

#header-menu {
display: flex;
gap: 1rem;

a {
padding: 0.5rem 1rem;

&:hover {
color: steelblue;
}
}
}
</style>
5 changes: 4 additions & 1 deletion stubs/theme/resources/js/main.js.vue.stub
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { createSSRApp, h } from "vue";
import { createInertiaApp } from "@inertiajs/vue3";
import { resolveInertiaPage } from "@evo-mark/inertia-wordpress";
import DefaultLayout from "./layouts/DefaultLayout.vue";
import "../css/style.postcss";

createInertiaApp({
resolve: resolveInertiaPage(
import.meta.glob("./pages/**/*.vue", { eager: false })
import.meta.glob("./pages/**/*.vue", { eager: false }),
DefaultLayout
),
setup({ el, App, props, plugin }) {
createSSRApp({ render: () => h(App, props) })
Expand Down
5 changes: 5 additions & 0 deletions stubs/theme/resources/js/pages/Archive.vue.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div>Archive Page</div>
</template>

<script setup></script>
5 changes: 5 additions & 0 deletions stubs/theme/resources/js/pages/Home.vue.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div>Home Page</div>
</template>

<script setup></script>
5 changes: 5 additions & 0 deletions stubs/theme/resources/js/pages/Post.vue.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div>Post Page</div>
</template>

<script setup></script>
Loading

0 comments on commit dd22b8b

Please sign in to comment.