Button Component

To create a button component, you can use any HTML element such as button or a tag. However, it is recommended to use the button element for actions and the a tag for navigation. Both elements can be easily styled with CSS or Tailwind CSS.

In this example, we will create a reusable button component. The button component can be customized with different styles and attributes. Here is an example of how to define the button component:

To get started, create a new file at src/app/Lib/PHPX/Components/Button.php and add the following code snippet:

The button component will accept the following attributes, along with any other attributes you wish to pass:

  • variant: Specifies the button's style variant (default, secondary, destructive, outline, ghost, link).
  • size: Specifies the button's size (default, sm, lg, icon).
  • type: Specifies the button's type attribute (button, submit, reset).
  • class: Custom CSS classes for additional styling.

Note: If the variant or size attributes are not specified, they will default to "default". Similarly, the type attribute defaults to "button" if not provided.

<?php

  namespace Lib\PHPX\Components;
  
  use Lib\PHPX\IPHPX;
  use Lib\PHPX\Utils;
  
  class Button implements IPHPX
  {
      /**
       * @var array<string, mixed> The properties or attributes passed to the component.
       */
      private array $props;
  
      /**
       * @var mixed The children elements or content to be rendered within the component.
       */
      private mixed $children;
  
      /**
       * @var string The CSS class for custom styling.
       */
      private string $class;
  
      /**
       * @var string Stores the precomputed CSS classes for the component.
       */
      private string $computedClass;
  
      /**
       * Constructor to initialize the component with the given properties.
       * 
       * @param array<string, mixed> $props Optional properties to customize the component.
       */
      public function __construct(array $props = [])
      {
          // echo "__construct: " . print_r($props, true);
  
          $this->props = $props;
          $this->children = $props['children'] ?? '';
          $this->class = $props['class'] ?? '';
  
          // Precompute the static classes once during construction
          $this->computedClass = $this->getComputedClasses();
      }
  
      /**
       * Registers or initializes any necessary components or settings. (Placeholder method).
       */
      public static function init(): void
      {
          // Register the component or any necessary initialization
      }
  
      /**
       * Combines and returns the CSS classes for the component.
       * 
       * @return string The merged CSS class string.
       */
      private function getComputedClasses(): string
      {
          $variant = $this->props['variant'] ?? 'default';
          $size = $this->props['size'] ?? 'default';
  
          // Base Tailwind CSS classes
          $baseClass = 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors   focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50';
  
          // Variant classes mapped to Tailwind CSS
          $variantClasses = [
              'default' => 'bg-blue-500 text-white hover:bg-blue-600',
              'destructive' => 'bg-red-500 text-white hover:bg-red-600',
              'outline' => 'border border-gray-300 bg-white hover:bg-gray-100 hover:text-gray-900',
              'secondary' => 'bg-gray-200 text-gray-800 hover:bg-gray-300',
              'ghost' => 'hover:bg-gray-100 hover:text-gray-900',
              'link' => 'text-blue-500 underline-offset-4 hover:underline',
          ];
  
          // Size classes
          $sizeClasses = [
              'default' => 'h-10 px-4 py-2',
              'sm' => 'h-9 px-3',
              'lg' => 'h-11 px-8',
              'icon' => 'h-10 w-10',
          ];
  
          // Merge the classes
          $classes = Utils::mergeClasses(
              $baseClass,
              $variantClasses[$variant] ?? $variantClasses['default'],
              $sizeClasses[$size] ?? $sizeClasses['default'],
              $this->class
          );
  
          return $classes;
      }
  
      /**
       * Generates and returns a string of HTML attributes from the provided props.
       * Excludes 'class' and 'children' props from being added as attributes.
       * 
       * @return string The generated HTML attributes.
       */
      private function getAttributes(): string
      {
          // Filter out 'class' and 'children' props
          $filteredProps = array_filter($this->props, function ($key) {
              return !in_array($key, ['class', 'children']);
          }, ARRAY_FILTER_USE_KEY);
  
          // Build attributes string by escaping keys and values
          $attributes = [];
          foreach ($filteredProps as $key => $value) {
              $escapedKey = htmlspecialchars($key, ENT_QUOTES, 'UTF-8');
              $escapedValue = htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
              $attributes[] = "$escapedKey='$escapedValue'";
          }
  
          return implode(' ', $attributes);
      }
  
      /**
       * Renders the component as an HTML string with the appropriate classes and attributes.
       * Also, allows for dynamic children rendering if a callable is passed.
       * 
       * @return string The final rendered HTML of the component.
       */
      public function render(): string
      {
          $attributes = $this->getAttributes();
          $class = $this->computedClass;
          $type = $this->props['type'] ?? 'button';
  
          return "<button type='$type' class='$class' $attributes>" . $this->children . "</button>";
      }
  
      /**
       * Converts the object to its string representation by rendering it.
       *
       * @return string The rendered HTML output of the component.
       */
      public function __toString(): string
      {
          return $this->render();
      }
  }

Now, you can integrate the Button component into your Prisma PHP application by following the example below:

<?php

  use Lib\PHPX\Components\Button;
  
  Button::init();
  
  ?>
  
  <div class="w-screen h-screen grid place-items-center">
      <div class="grid grid-cols-2 gap-4">
          <div class="flex flex-col gap-2">
              <Button>Default</Button>
              <Button variant="secondary">Secondary</Button>
              <Button variant="destructive">Destructive</Button>
              <Button variant="outline">Outline</Button>
              <Button variant="ghost">Ghost</Button>
              <Button variant="link">Link</Button>
          </div>
          <div class="flex flex-col gap-2">
              <Button>Default Size</Button>
              <Button variant="secondary" size="sm">Secondary Size sm</Button>
              <Button variant="destructive" size="lg">Destructive Size lg</Button>
              <Button variant="outline" size="icon">
                <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="currentColor"&  gt;
                    <path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224   224Z" />
                  </svg>
              </Button>
          </div>
      </div>
  </div>

By following the steps above, you have successfully created a reusable Button component using Prisma PHPX. You can now use this component in your application to display buttons with various styles and sizes.

In the render method, we use the button tag to render the button. You can customize the button by passing different props such as variant and size to achieve the desired look and feel.