{"_id":"@alexvdvalk/autocomplete-tags","_rev":"2-ed841d03ab1d8120769002f0e2ed411c","name":"@alexvdvalk/autocomplete-tags","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@alexvdvalk/autocomplete-tags","version":"1.0.0","keywords":["directus","directus-extension","directus-extension-interface","tags","autocomplete","multi-select","search"],"license":"MIT","_id":"@alexvdvalk/autocomplete-tags@1.0.0","maintainers":[{"name":"alexvdvalk","email":"alexvdvalk@gmail.com"}],"homepage":"https://github.com/alexvdvalk/autocomplete-tags-interface#readme","bugs":{"url":"https://github.com/alexvdvalk/autocomplete-tags-interface/issues"},"dist":{"shasum":"cecddf36a193c63c7617fb7fc25d8e00a4f091d6","tarball":"https://registry.npmjs.org/@alexvdvalk/autocomplete-tags/-/autocomplete-tags-1.0.0.tgz","fileCount":3,"integrity":"sha512-1AY8d/NroI7R2u3BWrZfK2xBrf9avJjiZffm8QcfobMvbw00mpP8lxzQy1DSmxJ0xWMMDMydJRCbOhT28N673A==","signatures":[{"sig":"MEQCIGTgbmwsKfFlUrbI+L4Un8+x9dcixD6Mir1cBl0YOBSHAiB0/SknGFH18HrJpLpHU/S/OcN0afMoKEDNAtsTZzg23g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26067},"icon":"article","type":"module","gitHead":"d85b28e055fe7d584de3219cef6ffdf41994ffdc","scripts":{"dev":"directus-extension build -w --no-minify","link":"directus-extension link","build":"directus-extension build","validate":"directus-extension validate"},"_npmUser":{"name":"alexvdvalk","email":"alexvdvalk@gmail.com"},"repository":{"url":"git+https://github.com/alexvdvalk/autocomplete-tags-interface.git","type":"git"},"_npmVersion":"11.6.2","description":"Search and select multiple items as tags from any Directus collection with autocomplete functionality","directories":{},"_nodeVersion":"22.20.0","_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.25","typescript":"^5.9.3","@directus/extensions-sdk":"17.0.3"},"directus:extension":{"host":"^10.10.0","path":"dist/index.js","type":"interface","source":"src/index.ts"},"_npmOperationalInternal":{"tmp":"tmp/autocomplete-tags_1.0.0_1764710301190_0.018106938324522925","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@alexvdvalk/autocomplete-tags","description":"Search and select multiple items as tags from any Directus collection with autocomplete functionality","icon":"article","version":"1.0.1","license":"MIT","keywords":["directus","directus-extension","directus-extension-interface","tags","autocomplete","multi-select","search"],"repository":{"type":"git","url":"git+https://github.com/alexvdvalk/autocomplete-tags-interface.git"},"type":"module","directus:extension":{"type":"interface","path":"dist/index.js","source":"src/index.ts","host":"^10.10.0"},"scripts":{"build":"directus-extension build","dev":"directus-extension build -w --no-minify","link":"directus-extension link","validate":"directus-extension validate","version":"npm run build && git add dist","preversion":"npm run validate","postversion":"git push && git push --tags","prepublishOnly":"npm run build","release:patch":"npm version patch && npm publish","release:minor":"npm version minor && npm publish","release:major":"npm version major && npm publish"},"devDependencies":{"@directus/extensions-sdk":"17.0.3","typescript":"^5.9.3","vue":"^3.5.25"},"gitHead":"26e95783eab498f68309549eb04ff23d5e8a816f","_id":"@alexvdvalk/autocomplete-tags@1.0.1","bugs":{"url":"https://github.com/alexvdvalk/autocomplete-tags-interface/issues"},"homepage":"https://github.com/alexvdvalk/autocomplete-tags-interface#readme","_nodeVersion":"22.20.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-7r1/D/UysPd6DOej+/HRBXZtWvjJIqzGxHxFWawT3ETzH9NEKPqrmKQxitAB//N68mKo0NW2eOLqQVovmTyXlw==","shasum":"a739aaf83be0e69a379c4beb7a0f23889afcdb78","tarball":"https://registry.npmjs.org/@alexvdvalk/autocomplete-tags/-/autocomplete-tags-1.0.1.tgz","fileCount":3,"unpackedSize":26405,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCZJE6nbYi2kc1bXyW5tjqkw57yo3Ng6dq4E5uD3Xs82gIgNlb0UWZgjr/vqq9DHl7jAEwgCick7aquXDn+jyZ5Is4="}]},"_npmUser":{"name":"alexvdvalk","email":"alexvdvalk@gmail.com"},"directories":{},"maintainers":[{"name":"alexvdvalk","email":"alexvdvalk@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/autocomplete-tags_1.0.1_1764757217189_0.8736374410180914"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-02T21:18:21.098Z","modified":"2025-12-03T10:20:17.561Z","1.0.0":"2025-12-02T21:18:21.379Z","1.0.1":"2025-12-03T10:20:17.369Z"},"bugs":{"url":"https://github.com/alexvdvalk/autocomplete-tags-interface/issues"},"license":"MIT","homepage":"https://github.com/alexvdvalk/autocomplete-tags-interface#readme","keywords":["directus","directus-extension","directus-extension-interface","tags","autocomplete","multi-select","search"],"repository":{"type":"git","url":"git+https://github.com/alexvdvalk/autocomplete-tags-interface.git"},"description":"Search and select multiple items as tags from any Directus collection with autocomplete functionality","maintainers":[{"name":"alexvdvalk","email":"alexvdvalk@gmail.com"}],"readme":"# Autocomplete Tags Interface (API)\n\nA custom Directus interface that combines autocomplete search functionality with multi-select tags. Search through **any external API** and select multiple items as tags, stored in JSON or CSV format.\n\nBased on the native [input-autocomplete-api](https://github.com/directus/directus/tree/main/app/src/interfaces/input-autocomplete-api) interface pattern.\n\n## Features\n\n- 🌐 **External API Search**: Query any REST API endpoint for autocomplete results\n- 🔍 **Real-time Search**: Debounced or throttled API requests\n- 🏷️ **Multi-select Tags**: Select multiple items and display them as removable tags\n- 📦 **Flexible Storage**: Store values as JSON array or CSV string\n- 🎯 **Path-based Extraction**: Use dot notation to extract values from nested API responses\n- ➕ **Custom Tags**: Optionally allow adding custom tags not from the API\n- ⚡ **Performance**: Configurable throttle/debounce with customizable rates\n- 🔧 **Flexible Configuration**: Works with any API structure\n\n## Installation\n\n1. The extension is already in your Directus extensions folder\n2. Build the extension:\n   ```bash\n   cd docker/extensions/autocomplete-tags\n   npm run build\n   ```\n3. Restart your Directus instance\n\n## Usage\n\n### Basic Setup\n\n1. In Directus Studio, go to **Settings > Data Model**\n2. Select your collection and add a new field\n3. Choose **Autocomplete Tags (API)** as the interface\n4. Configure the field:\n   - **Type**: Select `JSON` for array storage or `String/Text` for CSV storage\n   - **API URL**: Enter your API endpoint with `{{value}}` placeholder\n   - **Results Path**: Path to results array in API response\n   - **Text Path**: Field to display from each result\n   - **Value Path**: Field to store from each result\n\n### Configuration Options\n\n#### Required Options\n\n- **API URL**: The external API endpoint\n  - Example: `https://api.github.com/search/users?q={{value}}`\n  - Use `{{value}}` as placeholder for the search query\n  - The placeholder will be URL-encoded automatically\n\n#### Response Configuration\n\n- **Results Path**: Path to results array in API response\n  - Example: `data.results` or `items`\n  - Leave empty if the response itself is an array\n  - Supports nested paths with dot notation\n\n- **Text Path**: Path to display text in each result item\n  - Example: `name`, `title`, `login`\n  - Default: `name`\n  - Supports nested paths: `user.name`\n\n- **Value Path**: Path to value to store\n  - Example: `id`, `slug`, `login`\n  - Defaults to text path if not specified\n  - Supports nested paths: `user.id`\n\n#### Request Configuration\n\n- **Trigger**: When to trigger API requests\n  - **Debounce** (default): Wait for user to stop typing\n  - **Throttle**: Execute at regular intervals while typing\n\n- **Rate (ms)**: Milliseconds to wait before triggering\n  - Default: 500ms for debounce\n  - Recommendation: 300-800ms for debounce, 1000-2000ms for throttle\n\n#### Storage Options\n\n- **Storage Format**: Choose how to store the tags\n  - **JSON (Array)**: Stores as `[\"value1\", \"value2\"]` - recommended\n  - **CSV (Comma-separated)**: Stores as `\"value1, value2\"`\n\n#### Display Options\n\n- **Placeholder**: Custom placeholder text for the search input\n- **Icon Left/Right**: Add icons to the search input\n- **Allow Custom Tags**: Enable users to add custom tags not from the API\n\n### Example Configurations\n\n#### 1. GitHub Users Search\n\n```yaml\nField Type: JSON\nAPI URL: https://api.github.com/search/users?q={{value}}\nResults Path: items\nText Path: login\nValue Path: id\nStorage Format: json\nTrigger: debounce\nRate: 500\n```\n\n**API Response Structure:**\n```json\n{\n  \"items\": [\n    { \"login\": \"octocat\", \"id\": 583231 },\n    { \"login\": \"torvalds\", \"id\": 1024025 }\n  ]\n}\n```\n\n**Stored Value:** `[583231, 1024025]`\n\n#### 2. Movie Database (TMDB)\n\n```yaml\nField Type: JSON\nAPI URL: https://api.themoviedb.org/3/search/movie?api_key=YOUR_KEY&query={{value}}\nResults Path: results\nText Path: title\nValue Path: id\nStorage Format: json\n```\n\n**API Response:**\n```json\n{\n  \"results\": [\n    { \"id\": 550, \"title\": \"Fight Club\" },\n    { \"id\": 680, \"title\": \"Pulp Fiction\" }\n  ]\n}\n```\n\n#### 3. Simple Array Response\n\n```yaml\nField Type: JSON\nAPI URL: https://api.example.com/tags?search={{value}}\nResults Path: [leave empty]\nText Path: name\nValue Path: slug\n```\n\n**API Response (direct array):**\n```json\n[\n  { \"name\": \"JavaScript\", \"slug\": \"javascript\" },\n  { \"name\": \"TypeScript\", \"slug\": \"typescript\" }\n]\n```\n\n**Stored Value:** `[\"javascript\", \"typescript\"]`\n\n#### 4. Nested Data Structure\n\n```yaml\nAPI URL: https://api.example.com/search?q={{value}}\nResults Path: data.items\nText Path: attributes.name\nValue Path: id\n```\n\n**API Response:**\n```json\n{\n  \"data\": {\n    \"items\": [\n      { \"id\": \"abc\", \"attributes\": { \"name\": \"Item 1\" } },\n      { \"id\": \"def\", \"attributes\": { \"name\": \"Item 2\" } }\n    ]\n  }\n}\n```\n\n#### 5. OpenStreetMap Nominatim (Places)\n\n```yaml\nAPI URL: https://nominatim.openstreetmap.org/search?format=json&q={{value}}\nResults Path: [leave empty]\nText Path: display_name\nValue Path: place_id\nTrigger: debounce\nRate: 1000\n```\n\n#### 6. REST Countries API\n\n```yaml\nAPI URL: https://restcountries.com/v3.1/name/{{value}}\nResults Path: [leave empty]\nText Path: name.common\nValue Path: cca3\n```\n\n## API Response Requirements\n\nYour API should return:\n\n1. **JSON format** (required)\n2. **Array of items** (either directly or at a specified path)\n3. **Consistent structure** for all items\n\n### Supported Response Formats\n\n**Direct Array:**\n```json\n[\n  { \"id\": 1, \"name\": \"Item 1\" },\n  { \"id\": 2, \"name\": \"Item 2\" }\n]\n```\n\n**Nested Object:**\n```json\n{\n  \"data\": {\n    \"results\": [\n      { \"id\": 1, \"name\": \"Item 1\" }\n    ]\n  }\n}\n```\n\n**With Metadata:**\n```json\n{\n  \"items\": [...],\n  \"total\": 100,\n  \"page\": 1\n}\n```\n\n## Path Syntax\n\nUse dot notation to navigate nested objects:\n\n| Path | Accesses |\n|------|----------|\n| `name` | `{ name: \"value\" }` |\n| `user.name` | `{ user: { name: \"value\" } }` |\n| `data.items` | `{ data: { items: [...] } }` |\n| `attributes.display_name` | `{ attributes: { display_name: \"value\" } }` |\n\n## CORS Considerations\n\nSince this interface makes requests directly from the browser, the external API must support CORS (Cross-Origin Resource Sharing).\n\n### If the API doesn't support CORS:\n\n1. **Use a proxy**: Create a server-side proxy that forwards requests\n2. **Use a CORS proxy service** (for development only)\n3. **Request CORS support** from the API provider\n\n### Example Proxy Setup (Node.js/Express)\n\n```javascript\napp.get('/api/proxy', async (req, res) => {\n  const query = req.query.q;\n  const response = await fetch(`https://external-api.com/search?q=${query}`);\n  const data = await response.json();\n  res.json(data);\n});\n```\n\nThen use: `https://your-domain.com/api/proxy?q={{value}}`\n\n## Throttle vs Debounce\n\n### Debounce (Recommended for most cases)\n- Waits for user to stop typing\n- Makes request after specified delay\n- Best for: Text search, user input\n- Example: User types \"java\" → waits 500ms → makes request\n\n### Throttle\n- Executes at regular intervals\n- Makes request while user is still typing\n- Best for: Real-time updates, live search\n- Example: User types \"javascript\" → makes request every 1000ms\n\n## Field Type Recommendations\n\n| Storage Format | Recommended Field Type | SQL Type |\n|---------------|----------------------|----------|\n| JSON | JSON | `json` |\n| CSV | String or Text | `varchar` or `text` |\n\n## API Usage Examples\n\n### Querying Items with Tags\n\n```typescript\n// Find posts with specific tag\nconst posts = await directus.items('posts').readByQuery({\n  filter: {\n    tags: {\n      _contains: \"javascript\"\n    }\n  }\n});\n```\n\n### Creating Items with Tags\n\n```typescript\n// JSON format\nawait directus.items('posts').createOne({\n  title: 'My Post',\n  tags: ['javascript', 'vue', 'directus']\n});\n\n// CSV format\nawait directus.items('posts').createOne({\n  title: 'My Post',\n  tags: 'javascript, vue, directus'\n});\n```\n\n## Development\n\n### Building\n\n```bash\nnpm run build\n```\n\n### Watch Mode (Development)\n\n```bash\nnpm run dev\n```\n\n### Validation\n\n```bash\nnpm run validate\n```\n\n## Troubleshooting\n\n### No results showing up\n\n- **Check the API URL**: Test it in your browser with a sample value\n- **Verify Results Path**: Use browser dev tools to inspect the actual API response structure\n- **Check CORS**: Open browser console and look for CORS errors\n- **Test Text/Value Paths**: Ensure they match the actual field names in the API response\n\n### CORS errors\n\n```\nAccess to fetch at 'https://api.example.com' from origin 'https://your-directus.com' \nhas been blocked by CORS policy\n```\n\n**Solution**: Use a proxy endpoint or request CORS support from the API\n\n### API not being called\n\n- **Check Rate setting**: Make sure you're waiting long enough\n- **Verify URL format**: Ensure `{{value}}` placeholder is present\n- **Check browser console**: Look for JavaScript errors\n\n### Wrong values being stored\n\n- **Verify Value Path**: Check it matches your API response structure\n- **Test with browser dev tools**: Inspect the API response to confirm field names\n- **Try without Value Path**: Let it default to Text Path\n\n## Performance Tips\n\n1. **Set appropriate rate limits**:\n   - 300-500ms for debounce (general use)\n   - 500-1000ms for debounce (slower APIs)\n   - 1000-2000ms for throttle\n\n2. **Use specific paths**: Extract only what you need from API responses\n\n3. **Cache on API side**: If you control the API, implement caching\n\n4. **Consider rate limiting**: Some APIs have rate limits - use throttle to avoid hitting them\n\n## Security Considerations\n\n- **API Keys**: Don't expose API keys in the URL. Use a backend proxy instead\n- **Input Validation**: The search value is URL-encoded automatically\n- **XSS Protection**: Display values are handled by Vue's template system\n- **HTTPS**: Always use HTTPS endpoints in production\n\n## Example API Providers\n\nThese APIs work well with this interface:\n\n- **GitHub API**: Search users, repos, issues (no auth needed for basic search)\n- **REST Countries**: Country information\n- **OpenStreetMap Nominatim**: Place search\n- **TheMealDB**: Recipe search\n- **The Movie DB (TMDB)**: Movie/TV search (requires free API key)\n- **JSONPlaceholder**: Test API for development\n\n## Support\n\nFor issues or questions:\n1. Check this README\n2. Review QUICKSTART.md for setup\n3. Check browser console for errors\n4. Verify API response structure\n5. Test API endpoint independently\n\n## License\n\nMIT - This extension follows the same license as your Directus installation.\n","readmeFilename":"README.md"}