- Add equipment.js API client with full CRUD operations - Add organization sub-routes: account, department, discipline, instrument, site, workstation - Create EquipmentModal and DeleteConfirmModal components - Update master-data navigation and sidebar - Update tests, containers, counters, geography, locations, occupations, specialties, testmap, and valuesets pages - Add COMPONENT_ORGANIZATION.md documentation
4.4 KiB
4.4 KiB
Component Organization Guide
Guide for splitting large components and modals into manageable files.
When to Split Components
Split a component when:
- File exceeds 200 lines
- Component has multiple distinct sections (tabs, steps, panels)
- Logic becomes hard to follow
- Multiple developers work on different parts
Modal Organization Pattern
Structure for Large Modals
src/routes/(app)/feature/
├── +page.svelte # Main page
├── FeatureModal.svelte # Main modal container
└── feature-modal/ # Modal sub-components (kebab-case folder)
├── modals/ # Nested modals
│ └── PickerModal.svelte
└── tabs/ # Tab content components
├── BasicInfoTab.svelte
├── SettingsTab.svelte
└── AdvancedTab.svelte
Example: Test Form Modal
Location: src/routes/(app)/master-data/tests/test-modal/
<!-- TestFormModal.svelte -->
<script>
import Modal from '$lib/components/Modal.svelte';
import BasicInfoTab from './test-modal/tabs/BasicInfoTab.svelte';
import TechDetailsTab from './test-modal/tabs/TechDetailsTab.svelte';
import CalcDetailsTab from './test-modal/tabs/CalcDetailsTab.svelte';
let { open = $bindable(false), test = null } = $props();
let activeTab = $state('basic');
let formData = $state({});
</script>
<Modal bind:open title={test ? 'Edit Test' : 'New Test'} size="xl">
{#snippet children()}
<div class="tabs tabs-boxed mb-4">
<button class="tab" class:tab-active={activeTab === 'basic'} onclick={() => activeTab = 'basic'}>Basic</button>
<button class="tab" class:tab-active={activeTab === 'technical'} onclick={() => activeTab = 'technical'}>Technical</button>
<button class="tab" class:tab-active={activeTab === 'calculation'} onclick={() => activeTab = 'calculation'}>Calculation</button>
</div>
{#if activeTab === 'basic'}
<BasicInfoTab bind:formData />
{:else if activeTab === 'technical'}
<TechDetailsTab bind:formData />
{:else if activeTab === 'calculation'}
<CalcDetailsTab bind:formData />
{/if}
{/snippet}
</Modal>
<!-- test-modal/tabs/BasicInfoTab.svelte -->
<script>
let { formData = $bindable({}) } = $props();
</script>
<div class="space-y-4">
<div class="form-control">
<label class="label">Test Name</label>
<input class="input input-bordered" bind:value={formData.name} />
</div>
<div class="form-control">
<label class="label">Description</label>
<textarea class="textarea textarea-bordered" bind:value={formData.description}></textarea>
</div>
</div>
Data Flow
Parent to Child
- Pass data via props (
bind:formData) - Use
$bindable()for two-way binding - Keep state in parent when shared across tabs
Child to Parent
- Use callbacks for actions (
onSave,onClose) - Modify bound data directly (with
$bindable) - Emit events for complex interactions
Props Interface Pattern
// Define props with JSDoc
/** @type {{ formData: Object, onValidate: Function, readonly: boolean }} */
let {
formData = $bindable({}),
onValidate = () => true,
readonly = false
} = $props();
Naming Conventions
- Main modal:
{Feature}Modal.svelte(e.g.,TestFormModal.svelte) - Tab components:
{TabName}Tab.svelte(e.g.,BasicInfoTab.svelte) - Nested modals:
{Action}Modal.svelte(e.g.,ConfirmDeleteModal.svelte) - Folder names: kebab-case matching the modal name (e.g.,
test-modal/)
Shared State Management
For complex modals with shared state across tabs:
// In main modal
let sharedState = $state({
dirty: false,
errors: {},
selectedItems: []
});
// Pass to tabs
<TabName formData={formData} sharedState={sharedState} />
Import Order in Sub-components
Same as main components:
- Svelte imports
$lib/*imports- External libraries
- Relative imports (other tabs/modals)
Testing Split Components
# Test individual tab component
vitest run src/routes/feature/modal-tabs/BasicInfoTab.test.js
# Test main modal integration
vitest run src/routes/feature/FeatureModal.test.js
Benefits
- Maintainability: Each file has single responsibility
- Collaboration: Multiple developers can work on different tabs
- Testing: Test individual sections in isolation
- Performance: Only render visible tab content
- Reusability: Tabs can be used in different modals