redbastie/tailwire

此包已被废弃,不再维护。未建议替代包。

无需编写 HTML、CSS 或 JavaScript 即可构建响应式 Web 应用!由 Laravel Livewire 和 Tailwind 提供支持。

2.1.17 2021-02-23 22:28 UTC

README

请在这里查看我的新包: https://github.com/bastinald/malzahar

这是对该包所尝试实现的功能的更好实现。

Tailwire

无需编写 HTML、CSS 或 JavaScript 即可构建响应式 Web 应用!由 Laravel Livewire 和 Tailwind 提供支持。

想象一个不再需要在 HTML、CSS 和 JavaScript 之间不断切换上下文的世界。所有代码都可以存在于自包含的 PHP 组件类中。甚至您的数据库迁移逻辑也可以保留在模型中。这有点像 PHP 的 SwiftUI!

68747470733a2f2f692e696d6775722e636f6d2f6a4536765937302e706e67

要求

  • Laravel 8
  • PHP 8
  • NPM

功能

  • 使用 PHP 编写的表达式 HTML 构建器
  • 内置 Tailwind 样式和配置
  • Laravel Livewire 组件属性和动作连接
  • if、each、include 等语句的便捷指令
  • 安装、认证、组件、CRUD、模型和自动迁移的命令
  • 自动组件路由
  • 自动模型迁移
  • 自动用户时区
  • 轻松应用版本控制
  • PWA 功能(独立启动器、图标等)
  • 下拉刷新(iOS PWA)
  • 无限滚动
  • Heroicon 集成
  • 蜜罐垃圾邮件拒绝
  • & 更多!

使用的包

文档

链接

安装

创建一个新的 Laravel 8 项目

laravel new my-app

配置 .env 应用程序、数据库和邮件值

APP_*
DB_*
MAIL_*

通过 composer 需求 Tailwire

composer require redbastie/tailwire

安装 Tailwire

php artisan tailwire:install

命令

安装 Tailwire

php artisan tailwire:install

安装布局和索引组件、用户模型和工厂、配置文件、图标、PWA 清单、Tailwind CSS 和配置、JavaScript 资产、webpack,并运行必要的 NPM 命令。

生成认证脚手架

php artisan tailwire:auth

生成包含登录、注销、注册、密码重置和主页的认证组件。

生成组件

php artisan tailwire:component {class} {--full} {--modal} {--list} {--model=}

生成Tailwire组件。使用--full生成完整页面组件,使用--modal生成模态框,使用--list --model=ModelClass生成列表,或省略所有选项以生成基本组件。

示例

php artisan tailwire:component VehicleItem
php artisan tailwire:component VehiclePage --full
php artisan tailwire:component Admin/InsuranceDialog --modal
php artisan tailwire:component VehicleList --list --model=Vehicle

生成CRUD组件

php artisan tailwire:crud {class}

为指定的模型类生成CRUD组件。如果模型类不存在,它将自动创建,并包含工厂。

如果您指定User作为类,将生成完整的用户CRUD。

示例

php artisan tailwire:crud Vehicle
php artisan tailwire:crud Admin/Insurance

生成Tailwire模型

php artisan tailwire:model {class}

生成Tailwire模型和工厂,包含自动迁移方法和工厂定义。与其他命令一样,您可以指定子目录以存储类。

示例

php artisan tailwire:model Vehicle
php artisan tailwire:model Admin/Insurance

运行自动迁移

php artisan tailwire:migrate {--fresh} {--seed} {--force}

运行所有指定了migration方法的Tailwire模型的自动迁移。这使用Doctrine自动比较数据库并应用必要的更改。

可选地使用--fresh来清除数据库,并使用--seed来运行种子文件。如果希望在生产环境中运行,请使用--force

请注意,如果您仍然想使用传统的Laravel迁移文件,它们将在自动迁移方法之前运行。

用法

路由完整页面组件

class Login extends Component
{
    public $routeUri = '/login';
    public $routeName = 'login';
    public $routeMiddleware = 'guest';

在Tailwire组件中指定公共$route*属性,以启用自动路由。要启用组件的自动路由,至少需要$routeUri。可用的属性包括$routeUri$routeName$routeMiddleware(字符串或数组)、$routeDomain$routeWhere(数组)。

如果使用路由参数,请确保在mount方法中包含它们。

class Vehicle extends Component
{
    public $routeUri = '/vehicle/{vehicle}';
    public $routeName = 'vehicle';
    public $routeMiddleware = ['auth'];
    public $vehicle;
    
    public function mount(Vehicle $vehicle)
    {
        $this->vehicle = $vehicle;
    }

扩展布局组件

class Login extends Component
{
    public $viewTitle = 'Login';
    public $viewExtends = 'layouts.app';

指定一个$viewExtends属性,使用点符号来指向此组件将要扩展的组件。在此示例中,$viewExtends属性指向了Layouts/App组件。

Layouts/App组件中,使用$v->yield()方法以在内部渲染子组件。

class App extends Component
{
    public function view(View $v)
    {
        return $v->body(
            $v->header('Header content')->class('text-xl'),
            $v->yield(),
            $v->footer('Footer content')->class('text-sm'),
        )->class('bg-gray-100');

构建HTML元素

class Home extends Component
{
    public function view(View $v)
    {
        return $v->section(
            $v->h1('Home')->class('text-xl px-6 py-4'),
            $v->p('You are logged in!')->class('p-6')
        )->class('bg-white shadow divide-y');
    }

Tailwire使用表达式语法来构建HTML元素。如您所见,$v变量用于在视图中构建每个元素。一个$v链的第一个方法是HTML元素名称。可以在此处找到可用HTML元素名称的列表:HTML元素参考

第一种方法之后,每个附加的链式方法都代表元素的一个属性。例如,创建一个图像

$v->img()->src(asset('images/icon-fav.png'))->class('w-5 h-5')

这会将图像的 src 属性转换为资产 URL,并使用 Tailwind 类 w-5 h-5 进行样式化。可以在此处找到可用 HTML 元素属性的列表:HTML 属性参考

在这个例子中,你可以看到 img 方法不接受任何参数,因为它不使用闭合标签。使用闭合标签的元素,如 div,可以接受 ...$content 参数,你可以使用这些参数来构建内容

$v->div(
    $v->p('Hello')->class('text-red-600'),
    $v->p('World')->class('text-green-600'),
)->class('bg-blue-100')

通过 Tailwind 样式化元素

$v->icon('refresh')->class('animate-spin text-gray-400 w-5 h-5 mx-auto')

在链式 class() 方法中指定元素的 Tailwind 类。

现在你可能觉得,“但是没有自动完成!” 好消息;安装 VSCode 扩展“Tailwind CSS Intellisense”,然后向你的 settings.json 添加以下内容

"tailwindCSS.experimental.classRegex": [
    "class\\([\"']([^\"']*)[\"']\\)"
],

通过 Livewire 连接元素

除了 HTML 属性外,Tailwire 还允许你通过 Livewire 连接元素以使它们变得响应式。方法名称根据 Livewire 属性约定指定,具体可在此处找到:Laravel Livewire 文档

数据建模

$v->input()->type('email')->id('email')->wireModelDefer('email')

Tailwire 组件包含一个公共的 $model 数组,它将包含通过输入、选择等元素建模的数据。

你可以使用 $this->model() 辅助方法来获取数据

$email = $this->model('email');

如果你的模型数据是数组,你可以使用点符号从数组中获取值

$userName = $this->model('user.name');

你可以使用 $this->validate() 验证 $model 数据

$validated = $this->validate([
    'email' => ['required', 'email'],
]);

在检查验证错误时,你可以使用 $this->error() 方法来检查模型数据是否存在验证错误

$v->if($this->error('email'), 
    fn() => $v->p($this->error('email'))->class('text-xs text-red-600')
)

执行操作

class Counter extends Component
{
    public $count = 0;

    public function view(View $v) 
    {
        return $v->div(
            $v->button('Increment Count')->wireClick('incrementCount'),
            $v->p($this->count)->class('text-blue-600'),
        );
    }
    
    function incrementCount()
    {
        $this->count++;
    }

如你所见,你还可以通过 wire* 方法连接特定的操作。这包括点击、轮询、提交等。如果你的操作使用参数,只需在 wire* 方法中指定它们即可

public function view(View $v) 
{
    return $v->div(
        $v->button('Increment Count')->wireClick('incrementCount', 2),
        $v->p($this->count)->class('text-blue-600'),
    );
}

function incrementCount($amount)
{
    $this->count += $amount;
}

这种方法的优点是,它允许你将所有逻辑都保留在 Tailwire 组件类中!不再需要在大量文件和语言之间切换,并 wonder 事情发生在哪里。

使用指令 & 其他方法

View $v 变量还允许你在 view 方法中使用一些实用的指令,例如 if 语句、each 循环、包含等。

if 语句

$v->if(Auth::guest(),
    fn() => $v->p('You are signed out.')
)->else(
    fn() => $v->p('You are signed in!)
)

each 循环

$v->each(Vehicle::all(),
    fn(Vehicle $vehicle) => $v->div(
        $v->p($vehicle->name),
        $v->p($vehicle->created_at)->class('text-xs text-gray-600')
    )
)->empty(
    fn() => $v->p('No vehicles found.')
),

包含部分组件

$v->include('user-list-item', $user),

Heroicons (可在此处找到图标列表)

$v->icon('cog')->class('text-blue-600 w-5 h-5'),

下拉刷新指示器(适用于iOS PWA)

$v->swipeDownRefresh(
    $v->icon('refresh')->class('animate-spin text-gray-400 w-5 h-5 mx-auto')
)->class('mb-4'),

无限滚动指示器

$v->infiniteScroll(
    $v->icon('refresh')->class('animate-spin text-gray-400 w-5 h-5 mx-auto')
)->class('mt-4'),

附加功能

下拉刷新

$v->swipeDownRefresh(
    $v->icon('refresh')->class('animate-spin text-gray-400 w-5 h-5 mx-auto')
)->class('mb-4'),

如果用户已经从页面顶部向下滚动 -100px,则在整个页面重新加载之前,swipeDownRefresh 元素将短暂显示。这对于PWA非常有用,当用户将您的网络应用程序添加到其主屏幕并需要刷新页面的方法时。

无限滚动

public function view(View $v)
{
    return $v->section(
        $v->h1('Vehicles')->class('text-xl mb-2'),

        $v->each($this->query()->paginate($this->perPage),
            fn(Vehicle $vehicle) => $v->div(
                $v->p($vehicle->name),
                $v->p(timezone($vehicle->created_at))->class('text-xs text-gray-600')
            )->class('px-6 py-4')
        ),

        $v->if($this->query()->count() > $this->perPage,
            fn() => $v->infiniteScroll(
                $v->icon('refresh')->class('animate-spin text-gray-400 w-5 h-5 mx-auto')
            )->class('mt-4')
        )
    );
}

public function query()
{
    return Vehicle::query()->orderBy('name');
}

每个Tailwire组件都包含一个名为$perPage的公共属性,如果页面上存在隐藏的infiniteScroll元素,并且用户从底部向下滚动100px,则该属性会增加。增加后,组件应加载更多项并再次隐藏infiniteScroll元素。请参考上面的示例,了解如何使用query()paginate()count()$perPage

蜜罐垃圾邮件预防

$v->form(
    $v->div(
        $v->label('Email')->for('email'),
        $v->input()->type('email')->id('email')->wireModelDefer('email')
            ->class(($this->error('email') ? 'border-red-500' : 'border-gray-300') . ' w-full'),
        $v->if($this->error('email'), fn() => $v->p($this->error('email'))->class('text-xs text-red-600'))
    )->class('space-y-1'),

    $v->div(
        $v->label('Password')->for('password'),
        $v->input()->type('password')->id('password')->wireModelDefer('password')
            ->class(($this->error('password') ? 'border-red-500' : 'border-gray-300') . ' w-full'),
        $v->if($this->error('password'), fn() => $v->p($this->error('password'))->class('text-xs text-red-600'))
    )->class('space-y-1'),

    $v->honey(), // stop spam bots!

    $v->button('Register')->type('submit')->class('text-white bg-blue-600 w-full py-2')
)->wireSubmitPrevent('register')->class('space-y-4 p-6')

Tailwire使用Honey进行垃圾邮件机器人预防。有关该包的更多信息,请参阅该仓库。您还可以通过将true传递给元素来使用recaptcha。

$v->honey(true) // use honey with recaptcha (be sure to configure it!)