Skip to content
Open
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
8 changes: 7 additions & 1 deletion app/Http/Controllers/ShowDocumentationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,20 @@ public function __invoke(Request $request, string $platform, string $version, ?s
/**
* Cache the callback's result for a day, or compute it fresh in local so
* docs edits show up immediately without clearing (or racing on) the cache.
* The key folds in `config('docs')` so a Jump version bump invalidates
* rendered pages instead of trailing by up to a day.
*/
private function cacheOrCompute(string $key, Closure $callback): mixed
{
if (config('app.env') === 'local') {
return $callback();
}

return Cache::remember($key, now()->addDay(), $callback);
return Cache::remember(
$key.'_'.substr(md5(serialize(config('docs'))), 0, 8),
now()->addDay(),
$callback
);
}

public function serveRawMarkdown(Request $request, string $platform, string $version, string $page)
Expand Down
56 changes: 56 additions & 0 deletions app/Support/DocsLabels.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace App\Support;

/**
* Platform/version come from the route rather than being passed in, since
* BladeMarkdownPreprocessor hands page markdown to Blade with no view data
* beyond the current user.
*/
final class DocsLabels
{
public static function productName(): string
{
return match (request()->route('platform')) {
'mobile' => 'NativePHP for Mobile',
'desktop' => 'NativePHP for Desktop',
default => 'NativePHP',
};
}

/**
* Null when that version's tree has no versioning page — an unlinked
* label beats one that 404s.
*/
public static function versioningPolicyUrl(): ?string
{
return self::pageUrl('getting-started/versioning', 'version-labels');
}

public static function jumpUrl(): ?string
{
return self::pageUrl('the-basics/jump');
}

private static function pageUrl(string $page, ?string $fragment = null): ?string
{
$platform = request()->route('platform');
$version = request()->route('version');

if (blank($platform) || blank($version)) {
return null;
}

if (! file_exists(resource_path("views/docs/{$platform}/{$version}/{$page}.md"))) {
return null;
}

$url = route('docs.show', [
'platform' => $platform,
'version' => $version,
'page' => $page,
]);

return $fragment ? "{$url}#{$fragment}" : $url;
}
}
22 changes: 22 additions & 0 deletions app/Support/JumpApp.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,26 @@ public static function docsDeepLink(string $path): string
{
return self::CANONICAL_DOMAIN.'/'.ltrim($path, '/').'?'.self::QR_PARAM;
}

public static function currentVersion(): string
{
return (string) config('docs.jump.current_version');
}

/**
* `null`/`true` = no requirement, `false` = no Jump build has it, a
* version string = the minimum Jump version needed.
*/
public static function supports(string|bool|null $requirement): bool
{
if ($requirement === null || $requirement === true) {
return true;
}

if ($requirement === false) {
return false;
}

return version_compare(self::currentVersion(), $requirement, '>=');
}
}
46 changes: 46 additions & 0 deletions config/docs.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,52 @@
'mobile' => [],
],

/*
|--------------------------------------------------------------------------
| Released Minor Versions
|--------------------------------------------------------------------------
|
| Every minor release that exists, keyed by platform and then by major.
| The version labels rendered by <x-docs.version-badge> are checked against
| this list in the test suite, so a label can't quietly point at a version
| that was never released — or at one belonging to a different major after
| a page has been copied forward into a new version's tree.
|
| Add the new entry here as part of shipping a release.
|
*/

'released_versions' => [
'desktop' => [
1 => ['1.0'],
2 => ['2.0', '2.1', '2.2'],
],
'mobile' => [
1 => ['1.0', '1.1'],
2 => ['2.0'],
3 => ['3.0', '3.1', '3.2', '3.3'],
4 => ['4.0', '4.1', '4.2'],
],
],

/*
|--------------------------------------------------------------------------
| Jump
|--------------------------------------------------------------------------
|
| Jump ships on its own cadence, so a feature can be released in NativePHP
| and still not render when someone scans the QR code on a docs page.
|
| Pages and sections declare the Jump version they need; this value records
| what Jump currently ships. Bump it when Jump catches up and every label it
| now satisfies disappears on its own — no docs edits required.
|
*/

'jump' => [
'current_version' => '3.0',
],

/*
|--------------------------------------------------------------------------
| Renamed Documentation Pages
Expand Down
43 changes: 43 additions & 0 deletions resources/views/components/docs/badge.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@props([
'label',
'tooltip' => null,
'href' => null,
'variant' => 'neutral',
])

@php
$palettes = [
'neutral' => 'bg-gray-100 text-gray-600 ring-gray-200 dark:bg-white/10 dark:text-gray-300 dark:ring-white/15',
'info' => 'bg-sky-50 text-sky-700 ring-sky-200 dark:bg-sky-400/10 dark:text-sky-300 dark:ring-sky-400/25',
'warning' => 'bg-amber-50 text-amber-700 ring-amber-200 dark:bg-amber-400/10 dark:text-amber-300 dark:ring-amber-400/25',
'danger' => 'bg-rose-50 text-rose-700 ring-rose-200 dark:bg-rose-400/10 dark:text-rose-300 dark:ring-rose-400/25',
'jump' => 'bg-indigo-50 text-indigo-700 ring-indigo-200 dark:bg-indigo-400/10 dark:text-indigo-300 dark:ring-indigo-400/25',
];

// not-prose: keeps the typography plugin from restyling the pill in markdown.
$classes = implode(' ', [
'not-prose inline-flex select-none items-center whitespace-nowrap rounded-full',
'px-1.5 py-0.5 align-middle text-[11px] font-medium leading-4 no-underline',
'ring-1 ring-inset transition',
$palettes[$variant] ?? $palettes['neutral'],
]);
@endphp

@if (filled($href))
<a
href="{{ $href }}"
class="{{ $classes }} hover:ring-2"
@if (filled($tooltip))
title="{{ $tooltip }}"
aria-label="{{ $tooltip }}"
@endif
>{{ $label }}</a>
@else
<span
class="{{ $classes }}"
@if (filled($tooltip))
title="{{ $tooltip }}"
aria-label="{{ $tooltip }}"
@endif
>{{ $label }}</span>
@endif
19 changes: 19 additions & 0 deletions resources/views/components/docs/jump-badge.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@props([
'since' => null,
'unavailable' => false,
])

@php
$requirement = $unavailable ? false : $since;
@endphp

@unless (\App\Support\JumpApp::supports($requirement))
<x-docs.badge
variant="jump"
:label="$requirement === false ? 'Not in Jump yet' : 'Jump '.$requirement.'+'"
:tooltip="$requirement === false
? 'Not available in the Jump preview app yet — build to a device or simulator to try it'
: 'Needs Jump '.$requirement.' or later; Jump currently ships '.\App\Support\JumpApp::currentVersion()"
:href="\App\Support\DocsLabels::jumpUrl()"
/>
@endunless
34 changes: 34 additions & 0 deletions resources/views/components/docs/version-badge.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{{-- Never place inside a heading — HeadingRenderer slugs the heading's
rendered contents into the anchor id, so injected markup would change
existing deep links. --}}

@props([
'since' => null,
'changed' => null,
'deprecated' => null,
'removed' => null,
])

@php
$states = [
['version' => $since, 'variant' => 'neutral', 'prefix' => '', 'verb' => 'Added in'],
['version' => $changed, 'variant' => 'info', 'prefix' => 'Changed ', 'verb' => 'Changed in'],
['version' => $deprecated, 'variant' => 'warning', 'prefix' => 'Deprecated ', 'verb' => 'Deprecated in'],
['version' => $removed, 'variant' => 'danger', 'prefix' => 'Removed ', 'verb' => 'Removed in'],
];

$state = collect($states)->firstWhere(fn (array $state) => filled($state['version']));

// x.0 never renders — everything in a major's tree was there at x.0
// unless stated otherwise.
$minor = (int) (explode('.', (string) ($state['version'] ?? ''))[1] ?? 0);
@endphp

@if ($state && $minor > 0)
<x-docs.badge
:label="$state['prefix'].$state['version']"
:variant="$state['variant']"
:tooltip="$state['verb'].' '.\App\Support\DocsLabels::productName().' '.$state['version']"
:href="\App\Support\DocsLabels::versioningPolicyUrl()"
/>
@endif
30 changes: 25 additions & 5 deletions resources/views/docs/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@
@endpush

@php
// Jump previews EDGE components, which only exist in the Mobile v4 docs.
// Front matter `jump`: a version string, or false if no Jump build has it.
$jumpRequirement = $jump ?? null;

// Jump previews EDGE components (Mobile v4 only), and only where the
// shipping Jump can render the page — a QR to a blank screen is worse
// than no QR.
$showJumpPreview = $platform === 'mobile'
&& (string) $version === '4'
&& str_starts_with((string) request()->route('page'), 'edge-components/');
&& str_starts_with((string) request()->route('page'), 'edge-components/')
&& \App\Support\JumpApp::supports($jumpRequirement);
@endphp

<x-docs-layout>
Expand Down Expand Up @@ -58,9 +64,23 @@
:page="request()->route('page')"
/>

<h1 class="text-4xl font-semibold">
{{ $title }}
</h1>
<div class="flex flex-wrap items-center gap-x-3 gap-y-2">
<h1 class="text-4xl font-semibold">
{{ $title }}
</h1>

<x-docs.version-badge
:since="$since ?? null"
:changed="$changed ?? null"
:deprecated="$deprecated ?? null"
:removed="$removed ?? null"
/>

<x-docs.jump-badge
:since="is_string($jumpRequirement) ? $jumpRequirement : null"
:unavailable="$jumpRequirement === false"
/>
</div>

<x-docs.separator class="mt-4" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ the screen's layout chrome.

## Observing the lifecycle from outside

<x-docs.version-badge since="4.1" />

The hooks above are yours to override, which makes them the wrong place for anything cross-cutting. Put
analytics, telemetry, or crash breadcrumbs in a base class's `mount()` and any screen that defines its own
`mount()` silently replaces it — so the observer goes quiet on exactly the screens with the most logic in them.
Expand Down
4 changes: 2 additions & 2 deletions resources/views/docs/mobile/4/edge-components/layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,8 @@ The parser recognizes the classes listed below.
| Border color | `border-{palette}-{shade}`, `border-white`, `border-black`, `border-transparent`, `border-[#hex]`, `border-theme-{token}` |
| Border width | `border` (1dp), `border-2`, `border-4`, `border-8` |
| Rounded | `rounded` (4dp), `rounded-sm`, `rounded-md`, `rounded-lg`, `rounded-xl`, `rounded-2xl`, `rounded-3xl`, `rounded-full`, `rounded-[N]` |
| Rounded (per side) | `rounded-t-*`, `rounded-r-*`, `rounded-b-*`, `rounded-l-*` — each rounds that side's two corners. A bare side (`rounded-b`) uses the same 4dp default as `rounded` |
| Rounded (per corner) | `rounded-tl-*`, `rounded-tr-*`, `rounded-br-*`, `rounded-bl-*`, including arbitrary values (`rounded-br-[4]`) |
| Rounded (per side) <x-docs.version-badge since="4.2" /> | `rounded-t-*`, `rounded-r-*`, `rounded-b-*`, `rounded-l-*` — each rounds that side's two corners. A bare side (`rounded-b`) uses the same 4dp default as `rounded` |
| Rounded (per corner) <x-docs.version-badge since="4.2" /> | `rounded-tl-*`, `rounded-tr-*`, `rounded-br-*`, `rounded-bl-*`, including arbitrary values (`rounded-br-[4]`) |
| Shadow | `shadow`, `shadow-sm`, `shadow-md`, `shadow-lg`, `shadow-xl`, `shadow-2xl`, `shadow-inner`, `shadow-none` |
| Opacity | `opacity-{0..100}`, arbitrary `opacity-[0.5]` |
| Text size | `text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`, `text-3xl`, `text-4xl`, `text-5xl`, `text-6xl`, arbitrary `text-[N]` |
Expand Down
28 changes: 28 additions & 0 deletions resources/views/docs/mobile/4/getting-started/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,34 @@ with a full minimum patch release defined in your `composer.json`:

This automatically receives patch updates while giving you control over minor releases.

## Version labels

Anything documented in this version of the docs has been here since 4.0 unless it carries a label. Labels appear next
to a page title, under a section heading, or beside the individual prop or class they describe:

| Label | Meaning |
|-------|---------|
| <x-docs.version-badge since="4.2" /> | Added in that minor release. Upgrade to at least that version to use it |
| <x-docs.version-badge changed="4.2" /> | Behaviour or signature changed in that release — check it against what your app relies on before upgrading |
| <x-docs.version-badge deprecated="4.2" /> | Still works, but slated for removal. Move off it when convenient |
| <x-docs.version-badge removed="4.2" /> | Gone as of that release. Documented only so you know what replaced it |

Remember that a minor release [may contain native code changes](#minor-releases), so picking up a labelled feature
means rebuilding with `php artisan native:install --force` rather than a `composer update` alone.

### Jump labels

[Jump](../the-basics/jump) ships on its own release cadence, so a feature can be released in NativePHP and still not
render on your phone when you scan a QR code. Where that's the case, you'll see:

| Label | Meaning |
|-------|---------|
| <x-docs.jump-badge since="99.0" /> | Needs a newer Jump than the one on the stores. Build to a simulator or device to try it today |
| <x-docs.jump-badge unavailable /> | No Jump build supports it. It'll work in a packaged build of your app |

These disappear on their own as Jump catches up. Pages carrying one don't offer the "Preview in Jump" QR code, since
scanning it wouldn't show you the component.

## Your application versioning

Just because we're using semantic versioning for the `nativephp/mobile` package, doesn't mean your app must follow that
Expand Down
3 changes: 2 additions & 1 deletion resources/views/docs/mobile/4/the-basics/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Reading the current appearance and reacting to theme changes lives with the rest

<aside>

`System::flashlight()` is **deprecated** — use [`Device::flashlight()`](device#flashlight) instead.
`System::flashlight()` <x-docs.version-badge removed="4.1" /> has been removed — use
[`Device::flashlight()`](device#flashlight) instead.

</aside>
6 changes: 5 additions & 1 deletion tests/Feature/Docs/DocsCachingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public function test_non_local_docs_request_caches_page_properties(): void

$this->get('/docs/mobile/4/edge-components/stack')->assertStatus(200);

$this->assertTrue(Cache::has('docs_mobile_4_edge-components/stack'));
// The key is suffixed with a hash of config('docs') so a Jump version
// bump invalidates rendered pages instead of trailing by up to a day.
$key = 'docs_mobile_4_edge-components/stack_'.substr(md5(serialize(config('docs'))), 0, 8);

$this->assertTrue(Cache::has($key));
}
}
Loading