Skip to content

Commit

Permalink
React installation option (#73)
Browse files Browse the repository at this point in the history
* use common folder for inertia frontend

* adding react things

* formatting

* wip

* wip

* wip

* wip

* wip

* fix logout wording

* Apply fixes from StyleCI (#72)

* wip

* Apply fixes from StyleCI (#74)

* wip

* Apply fixes from StyleCI (#75)

* add transition / headless ui

* Apply fixes from StyleCI (#76)
  • Loading branch information
taylorotwell authored May 20, 2021
1 parent 81c9007 commit bd47593
Show file tree
Hide file tree
Showing 64 changed files with 1,275 additions and 26 deletions.
128 changes: 105 additions & 23 deletions src/Console/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ class InstallCommand extends Command
*
* @var string
*/
protected $signature = 'breeze:install
{--inertia : Indicates that the Inertia stack should be installed}
protected $signature = 'breeze:install {stack=blade : The development stack that should be installed}
{--composer=global : Absolute path to the Composer binary which should be used to install packages}';

/**
Expand All @@ -32,8 +31,12 @@ class InstallCommand extends Command
*/
public function handle()
{
if ($this->option('inertia')) {
return $this->installInertiaStack();
if ($this->argument('stack') === 'vue') {
return $this->installInertiaVueStack();
}

if ($this->argument('stack') === 'react') {
return $this->installInertiaReactStack();
}

// NPM Packages...
Expand Down Expand Up @@ -94,11 +97,11 @@ public function handle()
}

/**
* Install the Inertia Breeze stack.
* Install the Inertia Vue Breeze stack.
*
* @return void
*/
protected function installInertiaStack()
protected function installInertiaVueStack()
{
// Install Inertia...
$this->requireComposerPackages('inertiajs/inertia-laravel:^0.4.1', 'laravel/sanctum:^2.6', 'tightenco/ziggy:^1.0');
Expand All @@ -112,17 +115,17 @@ protected function installInertiaStack()
'@tailwindcss/forms' => '^0.2.1',
'@vue/compiler-sfc' => '^3.0.5',
'autoprefixer' => '^10.2.4',
'postcss' => '^8.2.1',
'postcss-import' => '^12.0.1',
'tailwindcss' => '^2.0.3',
'postcss' => '^8.2.13',
'postcss-import' => '^14.0.1',
'tailwindcss' => '^2.1.2',
'vue' => '^3.0.5',
'vue-loader' => '^16.1.2',
] + $packages;
});

// Controllers...
(new Filesystem)->ensureDirectoryExists(app_path('Http/Controllers/Auth'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia/app/Http/Controllers/Auth', app_path('Http/Controllers/Auth'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia-common/app/Http/Controllers/Auth', app_path('Http/Controllers/Auth'));

// Requests...
(new Filesystem)->ensureDirectoryExists(app_path('Http/Requests/Auth'));
Expand All @@ -131,39 +134,118 @@ protected function installInertiaStack()
// Middleware...
$this->installMiddlewareAfter('SubstituteBindings::class', '\App\Http\Middleware\HandleInertiaRequests::class');

copy(__DIR__.'/../../stubs/inertia/app/Http/Middleware/HandleInertiaRequests.php', app_path('Http/Middleware/HandleInertiaRequests.php'));
copy(__DIR__.'/../../stubs/inertia-common/app/Http/Middleware/HandleInertiaRequests.php', app_path('Http/Middleware/HandleInertiaRequests.php'));

// Views...
copy(__DIR__.'/../../stubs/inertia/resources/views/app.blade.php', resource_path('views/app.blade.php'));
copy(__DIR__.'/../../stubs/inertia-common/resources/views/app.blade.php', resource_path('views/app.blade.php'));

// Components + Pages...
(new Filesystem)->ensureDirectoryExists(resource_path('js/Components'));
(new Filesystem)->ensureDirectoryExists(resource_path('js/Layouts'));
(new Filesystem)->ensureDirectoryExists(resource_path('js/Pages'));

(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia/resources/js/Components', resource_path('js/Components'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia/resources/js/Layouts', resource_path('js/Layouts'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia/resources/js/Pages', resource_path('js/Pages'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia-vue/resources/js/Components', resource_path('js/Components'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia-vue/resources/js/Layouts', resource_path('js/Layouts'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia-vue/resources/js/Pages', resource_path('js/Pages'));

// Tests...
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/default/tests/Feature', base_path('tests/Feature'));

// Routes...
copy(__DIR__.'/../../stubs/inertia/routes/web.php', base_path('routes/web.php'));
copy(__DIR__.'/../../stubs/inertia/routes/auth.php', base_path('routes/auth.php'));
copy(__DIR__.'/../../stubs/inertia-common/routes/web.php', base_path('routes/web.php'));
copy(__DIR__.'/../../stubs/inertia-common/routes/auth.php', base_path('routes/auth.php'));

// "Dashboard" Route...
$this->replaceInFile('/home', '/dashboard', resource_path('js/Pages/Welcome.vue'));
$this->replaceInFile('Home', 'Dashboard', resource_path('js/Pages/Welcome.vue'));
$this->replaceInFile('/home', '/dashboard', app_path('Providers/RouteServiceProvider.php'));

// Tailwind / Webpack...
copy(__DIR__.'/../../stubs/inertia/tailwind.config.js', base_path('tailwind.config.js'));
copy(__DIR__.'/../../stubs/inertia/webpack.mix.js', base_path('webpack.mix.js'));
copy(__DIR__.'/../../stubs/inertia/webpack.config.js', base_path('webpack.config.js'));
copy(__DIR__.'/../../stubs/inertia/jsconfig.json', base_path('jsconfig.json'));
copy(__DIR__.'/../../stubs/inertia/resources/css/app.css', resource_path('css/app.css'));
copy(__DIR__.'/../../stubs/inertia/resources/js/app.js', resource_path('js/app.js'));
copy(__DIR__.'/../../stubs/inertia-common/tailwind.config.js', base_path('tailwind.config.js'));
copy(__DIR__.'/../../stubs/inertia-common/webpack.mix.js', base_path('webpack.mix.js'));
copy(__DIR__.'/../../stubs/inertia-common/webpack.config.js', base_path('webpack.config.js'));
copy(__DIR__.'/../../stubs/inertia-common/jsconfig.json', base_path('jsconfig.json'));
copy(__DIR__.'/../../stubs/inertia-common/resources/css/app.css', resource_path('css/app.css'));
copy(__DIR__.'/../../stubs/inertia-vue/resources/js/app.js', resource_path('js/app.js'));

$this->info('Breeze scaffolding installed successfully.');
$this->comment('Please execute the "npm install && npm run dev" command to build your assets.');
}

/**
* Install the Inertia React Breeze stack.
*
* @return void
*/
protected function installInertiaReactStack()
{
// Install Inertia...
$this->requireComposerPackages('inertiajs/inertia-laravel:^0.3.5', 'laravel/sanctum:^2.6', 'tightenco/ziggy:^1.0');

// NPM Packages...
$this->updateNodePackages(function ($packages) {
return [
'@headlessui/react' => '^1.2.0',
'@inertiajs/inertia' => '^0.8.4',
'@inertiajs/inertia-react' => '^0.5.12',
'@inertiajs/progress' => '^0.2.4',
'@tailwindcss/forms' => '^0.3.2',
'autoprefixer' => '^10.2.4',
'postcss' => '^8.2.13',
'postcss-import' => '^14.0.1',
'tailwindcss' => '^2.1.2',
'react' => '^17.0.2',
'react-dom' => '^17.0.2',
'@babel/preset-react' => '^7.13.13',
] + $packages;
});

// Controllers...
(new Filesystem)->ensureDirectoryExists(app_path('Http/Controllers/Auth'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia-common/app/Http/Controllers/Auth', app_path('Http/Controllers/Auth'));

// Requests...
(new Filesystem)->ensureDirectoryExists(app_path('Http/Requests/Auth'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/default/App/Http/Requests/Auth', app_path('Http/Requests/Auth'));

// Middleware...
$this->installMiddlewareAfter('SubstituteBindings::class', '\App\Http\Middleware\HandleInertiaRequests::class');

copy(__DIR__.'/../../stubs/inertia-common/app/Http/Middleware/HandleInertiaRequests.php', app_path('Http/Middleware/HandleInertiaRequests.php'));

// Views...
copy(__DIR__.'/../../stubs/inertia-common/resources/views/app.blade.php', resource_path('views/app.blade.php'));

// Components + Pages...
(new Filesystem)->ensureDirectoryExists(resource_path('js/Components'));
(new Filesystem)->ensureDirectoryExists(resource_path('js/Layouts'));
(new Filesystem)->ensureDirectoryExists(resource_path('js/Pages'));

(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia-react/resources/js/Components', resource_path('js/Components'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia-react/resources/js/Layouts', resource_path('js/Layouts'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/inertia-react/resources/js/Pages', resource_path('js/Pages'));

// Tests...
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/default/tests/Feature', base_path('tests/Feature'));

// Routes...
copy(__DIR__.'/../../stubs/inertia-common/routes/web.php', base_path('routes/web.php'));
copy(__DIR__.'/../../stubs/inertia-common/routes/auth.php', base_path('routes/auth.php'));

// "Dashboard" Route...
$this->replaceInFile('/home', '/dashboard', resource_path('js/Pages/Welcome.js'));
$this->replaceInFile('Home', 'Dashboard', resource_path('js/Pages/Welcome.js'));
$this->replaceInFile('/home', '/dashboard', app_path('Providers/RouteServiceProvider.php'));

// Tailwind / Webpack...
copy(__DIR__.'/../../stubs/inertia-common/tailwind.config.js', base_path('tailwind.config.js'));
copy(__DIR__.'/../../stubs/inertia-common/webpack.mix.js', base_path('webpack.mix.js'));
copy(__DIR__.'/../../stubs/inertia-common/webpack.config.js', base_path('webpack.config.js'));
copy(__DIR__.'/../../stubs/inertia-common/jsconfig.json', base_path('jsconfig.json'));
copy(__DIR__.'/../../stubs/inertia-common/resources/css/app.css', resource_path('css/app.css'));
copy(__DIR__.'/../../stubs/inertia-react/resources/js/app.js', resource_path('js/app.js'));

$this->replaceInFile('.vue()', '.react()', base_path('webpack.mix.js'));

$this->info('Breeze scaffolding installed successfully.');
$this->comment('Please execute the "npm install && npm run dev" command to build your assets.');
Expand Down
2 changes: 1 addition & 1 deletion stubs/default/resources/views/auth/verify-email.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
@csrf

<button type="submit" class="underline text-sm text-gray-600 hover:text-gray-900">
{{ __('Log out') }}
{{ __('Log Out') }}
</button>
</form>
</div>
Expand Down
4 changes: 2 additions & 2 deletions stubs/default/resources/views/layouts/navigation.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<x-dropdown-link :href="route('logout')"
onclick="event.preventDefault();
this.closest('form').submit();">
{{ __('Log out') }}
{{ __('Log Out') }}
</x-dropdown-link>
</form>
</x-slot>
Expand Down Expand Up @@ -91,7 +91,7 @@
<x-responsive-nav-link :href="route('logout')"
onclick="event.preventDefault();
this.closest('form').submit();">
{{ __('Log out') }}
{{ __('Log Out') }}
</x-responsive-nav-link>
</form>
</div>
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions stubs/inertia-react/resources/js/Components/Button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';

export default function Button({ type = 'submit', className = '', processing, children }) {
return (
<button
type={type}
className={
`inline-flex items-center px-4 py-2 bg-gray-900 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest active:bg-gray-900 transition ease-in-out duration-150 ${
processing && 'opacity-25'
} ` + className
}
disabled={processing}
>
{children}
</button>
);
}
13 changes: 13 additions & 0 deletions stubs/inertia-react/resources/js/Components/Checkbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

export default function Checkbox({ name, value, handleChange }) {
return (
<input
type="checkbox"
name={name}
value={value}
className="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
onChange={(e) => handleChange(e)}
/>
);
}
93 changes: 93 additions & 0 deletions stubs/inertia-react/resources/js/Components/Dropdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { useState, useContext } from 'react';
import { InertiaLink } from '@inertiajs/inertia-react';
import { Transition } from '@headlessui/react';

const DropDownContext = React.createContext();

const Dropdown = ({ children }) => {
const [open, setOpen] = useState(false);

const toggleOpen = () => {
setOpen((previousState) => !previousState);
};

return (
<DropDownContext.Provider value={{ open, setOpen, toggleOpen }}>
<div className="relative">{children}</div>
</DropDownContext.Provider>
);
};

const Trigger = ({ children }) => {
const { open, setOpen, toggleOpen } = useContext(DropDownContext);

return (
<>
<div onClick={toggleOpen}>{children}</div>

{open && <div className="fixed inset-0 z-40" onClick={() => setOpen(false)}></div>}
</>
);
};

const Content = ({ align = 'right', width = '48', contentClasses = 'py-1 bg-white', children }) => {
const { open, setOpen } = useContext(DropDownContext);

let alignmentClasses = 'origin-top';

if (align === 'left') {
alignmentClasses = 'origin-top-left left-0';
} else if (align === 'right') {
alignmentClasses = 'origin-top-right right-0';
}

let widthClasses = '';

if (width === '48') {
widthClasses = 'w-48';
}

return (
<>
<Transition
show={open}
enter="transition ease-out duration-200"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
{open && (
<div
className={`absolute z-50 mt-2 rounded-md shadow-lg ${alignmentClasses} ${widthClasses}`}
onClick={() => setOpen(false)}
>
<div className={`rounded-md ring-1 ring-black ring-opacity-5 ` + contentClasses}>
{children}
</div>
</div>
)}
</Transition>
</>
);
};

const Link = ({ href, method = 'post', as = 'a', children }) => {
return (
<InertiaLink
href={href}
method={method}
as={as}
className="block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out"
>
{children}
</InertiaLink>
);
};

Dropdown.Trigger = Trigger;
Dropdown.Content = Content;
Dropdown.Link = Link;

export default Dropdown;
9 changes: 9 additions & 0 deletions stubs/inertia-react/resources/js/Components/Label.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

export default function Label({ forInput, value, className, children }) {
return (
<label htmlFor={forInput} className={`block font-medium text-sm text-gray-700 ` + className}>
{value ? value : { children }}
</label>
);
}
17 changes: 17 additions & 0 deletions stubs/inertia-react/resources/js/Components/NavLink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { InertiaLink } from '@inertiajs/inertia-react';
import React from 'react';

export default function NavLink({ href, active, children }) {
return (
<InertiaLink
href={href}
className={
active
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out'
}
>
{children}
</InertiaLink>
);
}
Loading

0 comments on commit bd47593

Please sign in to comment.