Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,15 @@ The tappable trailing icon button on a [list item](../edge-components/list) take
headline="Backups"
trailingIconButton="info"
trailing-a11y-label="Backup details"
@trailing-press="showBackupInfo"
on-trailing-press="showBackupInfo"
/>
```
@endverbatim

`on-trailing-press` needs the `nativephp/mobile-ui` release that includes
[NativePHP/mobile-ui#108](https://github.com/NativePHP/mobile-ui/pull/108). On 0.6.0, use the fluent
`->onTrailingPress()` instead. See [List › Events](../edge-components/list#events).

List rows group their content (headline, supporting text, leading and trailing decorations) into a single
screen-reader focus stop; interactive trailing controls remain individually focusable.

Expand Down
15 changes: 10 additions & 5 deletions resources/views/docs/mobile/4/digging-deeper/data-binding.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ PHP side; change the property in PHP and the control reflects it on the next ren

@verbatim
```blade static
<native:text-input native:model="name" placeholder="Your name" />
<native:outlined-text-input native:model="name" placeholder="Your name" />
```
@endverbatim

Expand All @@ -29,7 +29,7 @@ render sees the current value.

Any input-style EDGE component binds with `native:model`:

- [`<native:text-input>`](../edge-components/text-input) — string
- [`<native:outlined-text-input>`, `<native:filled-text-input>` and `<native:bare-text-input>`](../edge-components/text-input) — string
- [`<native:toggle>`](../edge-components/toggle) / [`<native:checkbox>`](../edge-components/checkbox) — boolean
- [`<native:slider>`](../edge-components/slider) — float
- [`<native:radio-group>`](../edge-components/radio-group) / [`<native:select>`](../edge-components/select) — string
Expand All @@ -51,13 +51,18 @@ useful for text inputs where syncing on every character is wasteful:
| `native:model.lazy` | Alias for `.blur`. |
| `native:model.debounce.300ms` | After the user stops changing it for the given delay. |

For text the user types freely, prefer a short debounce such as `native:model.debounce.150ms`. A live binding sends
every keystroke to PHP, and when someone types quickly an earlier keystroke's value can come back after later ones and
overwrite them, so characters appear to vanish. See
[Text Input › Choosing a sync mode](../edge-components/text-input#choosing-a-sync-mode).

@verbatim
```blade static
{{-- Sync only when the user leaves the field --}}
<native:text-input native:model.blur="email" />
<native:outlined-text-input native:model.blur="email" />

{{-- Sync 500ms after typing stops --}}
<native:text-input native:model.debounce.500ms="search" />
<native:outlined-text-input native:model.debounce.500ms="search" />
```
@endverbatim

Expand Down Expand Up @@ -88,7 +93,7 @@ something other than a public property — set the value and change handler dire

@verbatim
```blade static
<native:text-input :value="$name" @change="rename" />
<native:outlined-text-input :value="$name" @change="rename" />
```
@endverbatim

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ value.

@verbatim
```blade static
<native:text-input native:model="query" />
<native:outlined-text-input native:model="query" />
```
@endverbatim

Expand Down
4 changes: 2 additions & 2 deletions resources/views/docs/mobile/4/digging-deeper/theming.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Every SuperNative app has one visual identity, defined in a single theme. Instea
element, you name **semantic tokens** — `primary`, `surface`, `on-surface` — and reference them everywhere. Change
a token once and it updates across every screen, in both light and dark mode.

The theme is provided by the `nativephp/native-ui` plugin (which ships the components), but it governs the whole
The theme is provided by the `nativephp/mobile-ui` plugin (which ships the components), but it governs the whole
app, so it's the visual contract for everything you build.

## Publishing the config
Expand Down Expand Up @@ -138,7 +138,7 @@ theme('primary', '#0F766E'); // fall back to a value when the token is unset
```

Pass a fallback when a setter needs a non-null string — `theme()` returns your default when the key is missing
(or `native-ui` isn't installed).
(or `nativephp/mobile-ui` isn't installed).

### Reacting to changes

Expand Down
2 changes: 1 addition & 1 deletion resources/views/docs/mobile/4/edge-components/icon.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ If you'd rather skip the `@@use` import, fully-qualified cases work anywhere:

<aside>

Three icon enums are generated into your app by the [native-ui](https://github.com/nativephp/native-ui) plugin:
Three icon enums are generated into your app by the [`nativephp/mobile-ui`](https://github.com/NativePHP/mobile-ui) plugin:
`App\Icons\Ios` (SF Symbols), `App\Icons\Android` (filled Material Icons), and `App\Icons\AndroidOutlined`
(outlined Material Icons — its cases tell the renderer to use the outlined Material font). Run the command below
once to create them, then reference any symbol as a typed, autocompletable case:
Expand Down
28 changes: 28 additions & 0 deletions resources/views/docs/mobile/4/edge-components/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,34 @@ It doesn't even rely on the web view!

</aside>

## Installing the components

Every element in this section comes from the `nativephp/mobile-ui` plugin. Core `nativephp/mobile` defines the
rendering pipeline and a few PHP element classes, but the SwiftUI and Compose renderers for all of these elements,
including `<native:text>` and `<native:column>`, ship in the plugin. Install and register it:

```shell
composer require nativephp/mobile-ui
php artisan vendor:publish --tag=nativephp-plugins-provider
php artisan native:plugin:register nativephp/mobile-ui
```

If the plugin isn't registered in `app/Providers/NativeServiceProvider.php`, plugin elements such as `button`,
`list_item` or `outlined_text_input` throw `Unknown native element type` when the screen renders, and the core-backed
ones (`text`, `column`, `row` and friends) render nothing on the device. The PHP classes live in the
`Native\Mobile\UI\Elements` namespace. See [Installation](../getting-started/installation#install-the-ui-components).

## Attribute names

Most attributes are written in kebab-case (`leading-icon`, `on-refresh`, `max-length`). A few elements, notably
[`<native:list-item>`](list#list-item), take camelCase attributes (`leadingIcon`, `trailingText`,
`leadingCheckbox`). Spell attributes exactly as each component's page lists them: an unrecognised spelling is
silently ignored rather than raising an error.

Boolean attributes follow PHP truthiness. A bare attribute (`disabled`) or a bound value (`:disabled="$busy"`) works
as you'd expect, but a literal string such as `disabled="false"` is a non-empty string and counts as `true`. Bind
booleans with `:` whenever the value can be false.

## The `native:` prefix is optional

`<native:column>` and `<column>` compile to exactly the same thing — the prefix is optional. We use it throughout
Expand Down
71 changes: 64 additions & 7 deletions resources/views/docs/mobile/4/edge-components/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ Accepts any EDGE elements as children. `<native:list-item>` is the canonical chi
A pre-styled Material3 row with a headline, optional supporting + overline text, and configurable leading + trailing
content slots.

<aside>

#### Attribute spelling

`<native:list-item>` takes its content, slot, color and elevation attributes in camelCase (`leadingIcon`,
`trailingText`, `leadingCheckbox`, `headlineColor`). The kebab-case spellings (`leading-icon`, `leading-checkbox`) are
silently ignored. Callback and array attributes are the exception and use kebab-case: `on-leading-change`,
`on-trailing-change`, `on-trailing-press`, `on-swipe-delete`, `leading-actions`, `trailing-actions`,
`trailing-badges`, `trailing-menu`, `trailing-a11y-label` and `headline-line-through`. Everything below is listed with
the spelling that works.

Bind boolean slots with `:` so a false value stays false: `:leadingCheckbox="$task->done"`. A literal
`leadingCheckbox="false"` is a non-empty string and renders a checked box.

</aside>

@verbatim
```blade
<native:list-item
Expand All @@ -63,6 +79,9 @@ content slots.
- `headline` - Primary text (required, string)
- `supporting` - Secondary text rendered below the headline (optional, string)
- `overline` - Small caption rendered above the headline (optional, string)
- `headline-line-through` - Strike through the headline, e.g. for a completed todo (optional, boolean, default:
`false`). Bind it: `:headline-line-through="$todo->done"`. Not in `nativephp/mobile-ui` 0.6.0; it needs the
release that includes [NativePHP/mobile-ui#109](https://github.com/NativePHP/mobile-ui/pull/109)

### Leading slot (mutually exclusive)

Expand All @@ -72,7 +91,7 @@ content slots.
- `leadingMonogram` - 1-2 character monogram (combine with `leadingMonogramColor`)
- `leadingMonogramColor` - Hex color for monogram background
- `leadingImage` - URL of a square image with a small radius
- `leadingCheckbox` - Boolean value for a leading checkbox. Interactive when `on-leading-change` is set —
- `leadingCheckbox` - Boolean value for a leading checkbox (bind it: `:leadingCheckbox="$done"`). Interactive when `on-leading-change` is set —
tapping the box fires your handler with the new value (the row's own `@press` still handles taps elsewhere
on the row); without a handler it renders as a static state glyph
- `leadingRadio` - Boolean value for a leading radio button. Interactive when `on-leading-change` is set;
Expand All @@ -86,10 +105,11 @@ content slots.
- `trailingCheckbox` - Boolean value for a trailing checkbox. Interactive when `on-trailing-change` is set;
static glyph otherwise
- `trailingSwitch` - Boolean value for a trailing switch [Android]
- `trailingIconButton` - Icon name for a tappable trailing button
- `trailingIconButton` - Icon name for a tappable trailing button. Handle taps with `on-trailing-press` (see
[Events](#events))
- `trailing-a11y-label` - Accessibility label for the trailing icon button (recommended whenever
`trailingIconButton` is set). See [Accessibility](../digging-deeper/accessibility)
- `trailing-menu` - Attach a tap-to-open dropdown to the row's trailing edge. When set without an explicit trailing
- `trailing-menu` - Attach a tap-to-open dropdown to the row's trailing edge (`trailingMenu` also works). When set without an explicit trailing
slot, an `ellipsis` icon button is auto-created as the anchor. See [Menus](menus)

Independent of the mutually-exclusive slot above, a row can also show a stack of small status icons:
Expand All @@ -109,9 +129,11 @@ All color props accept the full [color grammar](../digging-deeper/theming#color-
- `leadingIconColor`, `trailingIconColor`, `trailingTextColor` - Colors for the slot content
- `leadingIconBgColor` - Background color of the leading icon's circle

### State
### State and accessibility

- `disabled` - Disable the row (optional, boolean, default: `false`)
- `a11y-label` / `a11y-hint` - Screen-reader label and hint for the whole row. See
[Accessibility](../digging-deeper/accessibility)
- `tonalElevation` - Tonal elevation in dp [Android]
- `shadowElevation` - Shadow elevation in dp [Android]

Expand All @@ -124,8 +146,26 @@ All color props accept the full [color grammar](../digging-deeper/theming#color-
- `on-leading-change` / `on-trailing-change` - Component method called when the leading/trailing checkbox or
radio is toggled, receiving the new value. Without a handler the control renders as a static state glyph.

`onTrailingPress()` fires on both platforms when the trailing icon button is tapped. The trailing switch is
interactive on [Android] only.
- `on-trailing-press` - Component method called when the trailing icon button is tapped, on both platforms. This
attribute needs the `nativephp/mobile-ui` release that includes
[NativePHP/mobile-ui#108](https://github.com/NativePHP/mobile-ui/pull/108). In 0.6.0 it is silently ignored and
only the fluent `->onTrailingPress()` on a `ListItem` element works. `@trailing-press` never works: unknown `@name`
attributes are treated as child-component events and dropped from plain elements

The trailing switch is interactive on [Android] only.

Handlers receive their arguments in this order: the arguments you wrote in the expression, then the value the control
sends. So `on-leading-change="toggleTask(@{{ $task->id }})"` calls `toggleTask($id, $checked)`:

```php
public function toggleTask(int $id, bool $checked): void
{
Task::whereKey($id)->update(['done' => $checked]);
}
```

`on-swipe-delete` and `@press` send no value, so `on-swipe-delete="deleteTask(@{{ $task->id }})"` calls
`deleteTask($id)`. To drive these from a test, see [Testing a list](#testing-a-list).

### Swipe actions

Expand Down Expand Up @@ -224,7 +264,7 @@ The checkbox, swipe, and row press are three independent targets on one row: tap
<native:list-item
headline="{{ $task->title }}"
supporting="{{ $task->due }}"
leadingCheckbox="{{ $task->done }}"
:leadingCheckbox="$task->done"
on-leading-change="toggleTask({{ $task->id }})"
trailingIcon="forward"
on-swipe-delete="deleteTask({{ $task->id }})"
Expand Down Expand Up @@ -256,6 +296,23 @@ here just gives the demo enough rows to scroll before the end-reached trigger fi
```
@endverbatim

## Testing a list

In a [component test](../testing/introduction), target a row's callbacks by the expression you wrote in the template.
For the swipe-to-delete example above:

```php
use App\NativeComponents\Tasks;
use Native\Mobile\Testing\Native;

it('completes and deletes a task', function () {
Native::test(Tasks::class)
->check('toggleTask(1)') // tap the leading checkbox: calls toggleTask(1, true)
->press('deleteTask(1)') // swipe-to-delete fires a press at its callback
->press('openTask(2)'); // tap the row
});
```

## Element

```php
Expand Down
26 changes: 26 additions & 0 deletions resources/views/docs/mobile/4/edge-components/text-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,32 @@ automatically, so the `@{{ $name }}` echo updates as you type.
- `blur` — only fires on focus loss / submit
- `debounce` — fires after `debounce-ms` of inactivity (300ms when unset), or immediately on blur / submit

### Choosing a sync mode

Plain `native:model` is `live`: every keystroke is a round trip to PHP and a re-render. That's fine for short fields
and for screens that react to each character, but it has a cost when someone types quickly. With several keystrokes
in flight, the value PHP sends back for an earlier keystroke can land after the user has typed more, and in
`nativephp/mobile-ui` 0.6.0 that older value overwrites the field. The visible symptom is characters vanishing (and
sometimes coming back) while typing.

For free-typed text, debounce the binding so only one sync goes out once the user pauses:

@verbatim
```blade static
<native:outlined-text-input native:model.debounce.150ms="title" @submit="add" />
```
@endverbatim

A short window like 150ms still feels live to the user. Reach for:

- `native:model.debounce.150ms` for text the user types freely: titles, notes, chat messages, new-item fields
- `native:model.debounce.300ms` (or longer) for search and filter boxes that trigger a query
- `native:model.blur` for form fields you only read when the form is submitted
- plain `native:model` only when you need every character, such as a character counter, and the field is short

Debounced and blurred fields flush any pending change on submit and on focus loss, before `@submit` fires. The
`@submit` handler also receives the field's text as its last argument, so `@submit="add"` calls `add($text)`.

## Caret and selection reporting

`@selectionChange` reports caret position and text selection back to your component — for the cases where `@change`
Expand Down
7 changes: 6 additions & 1 deletion resources/views/docs/mobile/4/getting-started/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ with `php artisan native:plugin:register`.

</aside>

The debug builds `native:run` produces are left on disk: the iOS simulator app at
`nativephp/ios/build/Build/Products/Debug-iphonesimulator/NativePHP-simulator.app` and the Android APK at
`nativephp/android/app/build/outputs/apk/debug/app-debug.apk`. See
[Finding the built app](development#finding-the-built-app).

### native:watch

Watch for file changes and sync to a running mobile app.
Expand Down Expand Up @@ -403,7 +408,7 @@ php artisan native:plugin:install-agent
### native-ui:generate-icons

Generate the `App\Icons\Ios`, `App\Icons\Android`, and `App\Icons\AndroidOutlined` enums so you can reference icons as
typed, autocompletable enum cases. Ships with the [native-ui](https://github.com/nativephp/native-ui) plugin.
typed, autocompletable enum cases. Ships with the [`nativephp/mobile-ui`](https://github.com/NativePHP/mobile-ui) plugin.

```shell
php artisan native-ui:generate-icons
Expand Down
27 changes: 27 additions & 0 deletions resources/views/docs/mobile/4/getting-started/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,33 @@ It's better than scratching your head for an hour trying to figure out why your

</aside>

## Finding the built app

`native:run` builds a normal debug app and then installs it. The build output stays on disk afterwards, so you can
hand it to a teammate, install it on another simulator or emulator, or upload it to a device farm.

| Platform | Command | Output |
|----------|---------|--------|
| iOS simulator | `php artisan native:run ios {simulator-udid}` | `nativephp/ios/build/Build/Products/Debug-iphonesimulator/NativePHP-simulator.app` |
| Android debug | `php artisan native:run android --build=debug` | `nativephp/android/app/build/outputs/apk/debug/app-debug.apk` |

A few things to know:

- The simulator `.app` is always named `NativePHP-simulator.app`, whatever your app is called. NativePHP sets the
bundle name inside it from your `APP_NAME`. It only runs on simulators, not on physical devices.
- Install the `.app` on any booted simulator with `xcrun simctl install booted path/to/NativePHP-simulator.app`, and
the `.apk` on any emulator or device with `adb install -r path/to/app-debug.apk`.
- `--build=profileable` on Android writes `apk/profileable/app-profileable.apk` instead.
- The `nativephp` directory is rebuilt by `native:install --force`, so copy anything you want to keep somewhere else.
- Keep `NATIVEPHP_APP_VERSION=DEBUG` in `.env` for these builds. A `DEBUG` build re-extracts your Laravel app every
time it launches. With a fixed version such as `1.0.0`, an app that is already installed only re-extracts when the
version or build number changes, so installing a fresh build over an old one can keep showing the old screens.
- These are development builds. For signed release builds (`.ipa`, release `.apk` or `.aab`), use
[`native:package`](commands#nativepackage) or [Bifrost](../publishing/bifrost).

To find a simulator's UDID, run `xcrun simctl list devices available`. For Android device serials, run
`adb devices`.

## Working with Xcode or Android Studio

On occasion, it is useful to compile your app from inside the target platform's dedicated development tools, Android
Expand Down
Loading
Loading