arcgis-widgets-ui について
arcgis-widgets-uiは、ArcGIS Map Components、Core API widgets、Calciteを使った地図UI構築のスキルです。地図アプリのUIを開発する人向けに、コンポーネント選択、配置、イベント、設定時の注意点を解説します。
主な機能
- 01マップ・シーンコンポーネントの利用
- 02レイヤー・検索・凡例ウィジェットの設定
- 03スロットによるコンポーネント配置
- 04Calcite UIコンポーネントの統合
- 05ウィジェットイベントとスタイルの処理
活用シーン
- 01地図アプリに検索と凡例を追加
- 02折りたたみ式の地図ウィジェットパネルを構築
- 03地図画面にタイムスライダーを表示
- 04Calciteでアプリのレイアウトを構成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ saschabrunnerch/ arcgis-maps-sdk-js-ai-context --skill arcgis-widgets-uiインストール後、Claude Code で/arcgis-widgets-uiを入力して読み込まれたか確認します。
同梱ファイルAGENTS.md
詳細
ArcGIS ウィジェットと UI
ウィジェット、Map Components、Calcite を使ってユーザーインターフェースを構築する際に、このスキルを使用してください。
ベストプラクティス: Core API ウィジェットよりも、Map Components(
arcgis-legendやarcgis-searchなどの Web コンポーネント)を優先してください。Esri は Web コンポーネントを主要なアプローチとして採用しています。
インポートパターン
Map Components(ESM)
import "@arcgis/map-components/dist/components/arcgis-map";
import "@arcgis/map-components/dist/components/arcgis-legend";
import "@arcgis/map-components/dist/components/arcgis-search";
import "@arcgis/map-components/dist/components/arcgis-layer-list";
import "@arcgis/map-components/dist/components/arcgis-expand";
Core API ウィジェット(ESM)
import Legend from "@arcgis/core/widgets/Legend.js";
import LayerList from "@arcgis/core/widgets/LayerList.js";
import Search from "@arcgis/core/widgets/Search.js";
import BasemapGallery from "@arcgis/core/widgets/BasemapGallery.js";
動的インポート(CDN)
const Legend = await $arcgis.import("@arcgis/core/widgets/Legend.js");
const Search = await $arcgis.import("@arcgis/core/widgets/Search.js");
注: Map Components の CSS は npm 経由で自動的に読み込まれます。Core API ウィジェットでは、
@import "@arcgis/core/assets/esri/themes/light/main.css";が必要です。
マップコンポーネントの概要
| コンポーネント | 目的 |
|---|---|
arcgis-map | 2D マップコンテナー |
arcgis-scene | 3D シーンコンテナー |
arcgis-zoom | 拡大/縮小ボタン |
arcgis-compass | 方位インジケーター |
arcgis-home | 初期範囲に戻る |
arcgis-locate | ユーザーの位置を検索 |
arcgis-track | ユーザーの位置を追跡 |
arcgis-navigation-toggle | パン/回転モード(3D) |
arcgis-floor-filter | フロアレベルで屋内マップをフィルター処理 |
arcgis-fullscreen | 全画面表示を切り替え |
arcgis-scale-bar | マップの縮尺を表示 |
arcgis-legend | レイヤーのシンボル凡例 |
arcgis-layer-list | レイヤーの表示設定 |
arcgis-basemap-gallery | ベースマップを切り替え |
arcgis-basemap-layer-list | ベースマップレイヤーのレイヤーリスト |
arcgis-basemap-toggle | 2 つのベースマップを切り替え |
arcgis-catalog-layer-list | CatalogLayer のサブレイヤーを参照 |
arcgis-search | 位置検索 |
arcgis-placement | ウィジェットの配置を制御 |
arcgis-popup | フィーチャーのポップアップ |
arcgis-editor | フィーチャーの編集 |
arcgis-feature | ポップアップなしでフィーチャー情報を表示 |
arcgis-feature-form | フォームベースの属性編集 |
arcgis-features | 複数のフィーチャー情報を表示 |
arcgis-sketch | ジオメトリーを描画 |
arcgis-feature-table | 表形式のデータビュー |
arcgis-time-slider | 時間的なナビゲーション |
arcgis-expand | 折りたたみ可能なコンテナー |
arcgis-print | マップの印刷 |
arcgis-table-list | フィーチャーテーブルの一覧表示と管理 |
arcgis-bookmarks | ブックマークに移動 |
arcgis-directions | ターンごとのルート案内 |
arcgis-swipe | レイヤーを比較 |
arcgis-coordinate-conversion | 座標形式 |
arcgis-daylight | 3D ライティングを制御 |
arcgis-weather | 3D 気象効果 |
arcgis-distance-measurement-2d | 2D 距離の計測 |
arcgis-area-measurement-2d | 2D 面積の計測 |
arcgis-direct-line-measurement-3d | 3D 線の計測 |
arcgis-area-measurement-3d | 3D 面積の計測 |
arcgis-elevation-profile | パスに沿った標高プロファイル |
arcgis-line-of-sight | 見通し解析(3D) |
arcgis-slice | 3D データをスライス |
arcgis-shadow-cast | 影の投影解析(3D) |
arcgis-oriented-imagery-viewer | 指向性画像を表示 |
arcgis-video-player | 動画フィードを再生 |
arcgis-link-chart | リンクチャートの可視化 |
arcgis-utility-network-trace | ユーティリティーネットワークのトレース |
arcgis-version-management | ジオデータベースのバージョンを管理 |
スロットベースの配置
<arcgis-map basemap="streets-vector">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-home slot="top-left"></arcgis-home>
<arcgis-compass slot="top-left"></arcgis-compass>
<arcgis-search slot="top-right"></arcgis-search>
<arcgis-layer-list slot="top-right"></arcgis-layer-list>
<arcgis-legend slot="bottom-left"></arcgis-legend>
<arcgis-scale-bar slot="bottom-right"></arcgis-scale-bar>
<arcgis-popup slot="popup"></arcgis-popup>
</arcgis-map>
利用可能なスロット:top-left、top-right、bottom-left、bottom-right、top-start、top-end、bottom-start、bottom-end、popup
同じスロットに配置されたウィジェットは、DOM の順序で垂直に積み重なります。
Expand コンポーネント
折りたたみ可能にするには、ウィジェットを arcgis-expand で囲みます:
<arcgis-map basemap="streets-vector">
<arcgis-expand slot="top-right" expand-tooltip="Show Legend" mode="floating">
<arcgis-legend></arcgis-legend>
</arcgis-expand>
<arcgis-expand slot="top-left" expanded>
<arcgis-layer-list></arcgis-layer-list>
</arcgis-expand>
</arcgis-map>
参照要素(外部コンポーネント)
コンポーネントをマップの外に配置し、参照します:
<calcite-shell>
<calcite-shell-panel slot="panel-start">
<arcgis-legend reference-element="arcgis-map"></arcgis-legend>
</calcite-shell-panel>
<arcgis-map id="arcgis-map" basemap="topo-vector">
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
</calcite-shell>
よく使われるウィジェット
Legend
<arcgis-legend slot="bottom-left"></arcgis-legend>
// Core API
import Legend from "@arcgis/core/widgets/Legend.js";
const legend = new Legend({
view: view,
layerInfos: [
{
layer: featureLayer,
title: "Custom Title",
},
],
});
view.ui.add(legend, "bottom-left");
LayerList
<arcgis-layer-list slot="top-right"></arcgis-layer-list>
// Core API with actions
import LayerList from "@arcgis/core/widgets/LayerList.js";
const layerList = new LayerList({
view: view,
listItemCreatedFunction: (event) => {
const item = event.item;
item.actionsSections = [
[
{
title: "Zoom to layer",
icon: "zoom-to-object",
id: "zoom-to",
},
],
];
},
});
layerList.on("trigger-action", (event) => {
if (event.action.id === "zoom-to") {
view.goTo(event.item.layer.fullExtent);
}
});
view.ui.add(layerList, "top-right");
BasemapGallery
<arcgis-basemap-gallery slot="top-right"></arcgis-basemap-gallery>
// Core API
import BasemapGallery from "@arcgis/core/widgets/BasemapGallery.js";
const basemapGallery = new BasemapGallery({ view: view });
view.ui.add(basemapGallery, "top-right");
Search
<arcgis-search slot="top-right"></arcgis-search>
// Core API with custom sources
import Search from "@arcgis/core/widgets/Search.js";
const search = new Search({
view: view,
sources: [
{
layer: featureLayer,
searchFields: ["name", "address"],
displayField: "name",
exactMatch: false,
outFields: ["*"],
name: "My Layer",
placeholder: "Search features",
},
],
});
view.ui.add(search, "top-right");
// Events
search.on("select-result", (event) => {
console.log("Selected:", event.result);
});
FeatureTable
<arcgis-feature-table reference-element="arcgis-map"></arcgis-feature-table>
// Core API
import FeatureTable from "@arcgis/core/widgets/FeatureTable.js";
const featureTable = new FeatureTable({
view: view,
layer: featureLayer,
container: "tableDiv",
visibleElements: {
header: true,
columnMenus: true,
selectionColumn: true,
},
tableTemplate: {
columnTemplates: [
{ fieldName: "name", label: "Name" },
{ fieldName: "population", label: "Population" },
],
},
});
// Watch selection via highlightIds
featureTable.highlightIds.on("change", (event) => {
console.log("Selected IDs:", featureTable.highlightIds.toArray());
});
TimeSlider
<arcgis-time-slider
slot="bottom-right"
layout="auto"
mode="time-window"
time-visible
loop
>
</arcgis-time-slider>
<script type="module">
const timeSlider = document.querySelector("arcgis-time-slider");
const mapEl = document.querySelector("arcgis-map");
await mapEl.viewOnReady();
await layer.load();
timeSlider.fullTimeExtent = layer.timeInfo.fullTimeExtent;
timeSlider.stops = { interval: layer.timeInfo.interval };
</script>
// Core API
import TimeSlider from "@arcgis/core/widgets/TimeSlider.js";
const timeSlider = new TimeSlider({
view: view,
mode: "time-window", // instant, time-window, cumulative-from-start, cumulative-from-end
fullTimeExtent: layer.timeInfo.fullTimeExtent,
stops: {
interval: { value: 1, unit: "hours" },
},
playRate: 1000,
loop: true,
});
view.ui.add(timeSlider, "bottom-right");
// Events
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
reactiveUtils.watch(
() => timeSlider.timeExtent,
(timeExtent) => {
console.log("Time changed:", timeExtent.start, timeExtent.end);
},
);
コアウィジェットのアプローチ
ビューへのウィジェットの追加
import Legend from "@arcgis/core/widgets/Legend.js";
const legend = new Legend({ view: view });
// Add to view UI
view.ui.add(legend, "bottom-left");
// Add multiple widgets
view.ui.add([
{ component: legend, position: "bottom-left" },
{ component: search, position: "top-right" },
]);
// Add at specific index (order in position)
view.ui.add(legend, { position: "bottom-left", index: 0 });
// Remove widget
view.ui.remove(legend);
カスタムコンテナ内のウィジェット
<div id="legendDiv"></div>
<script type="module">
import Legend from "@arcgis/core/widgets/Legend.js";
const legend = new Legend({
view: view,
container: "legendDiv",
});
</script>
Calcite Design System の統合
Calcite を使った基本的なレイアウト
<!DOCTYPE html>
<html>
<head>
<link
rel="stylesheet"
href="https://js.arcgis.com/5.0/@arcgis/core/assets/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/5.0/"></script>
<script
type="module"
src="https://js.arcgis.com/map-components/5.0/arcgis-map-components.esm.js"
></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
</head>
<body class="calcite-mode-light">
<calcite-shell>
<calcite-navigation slot="header">
<calcite-navigation-logo
slot="logo"
heading="My Map App"
></calcite-navigation-logo>
</calcite-navigation>
<calcite-shell-panel slot="panel-start">
<calcite-panel heading="Layers">
<arcgis-layer-list reference-element="map"></arcgis-layer-list>
</calcite-panel>
</calcite-shell-panel>
<arcgis-map id="map" basemap="streets-vector">
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
<calcite-shell-panel slot="panel-end">
<calcite-panel heading="Legend">
<arcgis-legend reference-element="map"></arcgis-legend>
</calcite-panel>
</calcite-shell-panel>
</calcite-shell>
</body>
</html>
Calcite アクションバー
<calcite-shell>
<calcite-shell-panel slot="panel-start">
<calcite-action-bar slot="action-bar">
<calcite-action
icon="layers"
text="Layers"
data-panel="layers"
></calcite-action>
<calcite-action
icon="legend"
text="Legend"
data-panel="legend"
></calcite-action>
<calcite-action
icon="bookmark"
text="Bookmarks"
data-panel="bookmarks"
></calcite-action>
</calcite-action-bar>
<calcite-panel id="layers" heading="Layers">
<arcgis-layer-list reference-element="map"></arcgis-layer-list>
</calcite-panel>
<calcite-panel id="legend" heading="Legend" hidden>
<arcgis-legend reference-element="map"></arcgis-legend>
</calcite-panel>
</calcite-shell-panel>
<arcgis-map id="map" basemap="topo-vector"></arcgis-map>
</calcite-shell>
<script>
document.querySelectorAll("calcite-action").forEach((action) => {
action.addEventListener("click", () => {
const panelId = action.dataset.panel;
document.querySelectorAll("calcite-panel").forEach((panel) => {
panel.hidden = panel.id !== panelId;
});
});
});
</script>
よく使われる Calcite コンポーネント
| コンポーネント | 用途 |
|---|---|
calcite-shell | アプリのレイアウトコンテナ |
calcite-shell-panel | サイドパネル |
calcite-panel | コンテンツパネル |
calcite-navigation | ヘッダー/フッター |
calcite-action-bar | アイコンボタンバー |
calcite-action | アイコンボタン |
calcite-button | 標準ボタン |
calcite-input | テキスト入力 |
calcite-list | リストコンテナ |
calcite-list-item | リスト項目 |
calcite-card | カードコンテナ |
calcite-modal | モーダルダイアログ |
calcite-alert | アラートメッセージ |
calcite-loader | 読み込みインジケーター |
テーマ設定
<body class="calcite-mode-light">
<body class="calcite-mode-dark">
<style>
:root {
--calcite-color-brand: #007ac2;
--calcite-color-brand-hover: #005a8e;
--calcite-color-text-1: #323232;
}
</style>
</body>
</body>
ウィジェットイベント
// Search select
search.on("select-result", (event) => {
console.log(event.result);
});
// LayerList trigger action
layerList.on("trigger-action", (event) => {
console.log(event.action, event.item);
});
// FeatureTable selection
featureTable.highlightIds.on("change", (event) => {
console.log(event.added, event.removed);
});
参考サンプル
legend- レイヤーのシンボル表現に使う Legend ウィジェットwidgets-layerlist- レイヤー管理に使う LayerList ウィジェットwidgets-search-multiplesource- 複数のソースを持つ Search ウィジェットwidgets-featuretable- FeatureTable ウィジェットの統合basemap-gallery- ベースマップの切り替えに使う BasemapGallerywidgets-timeslider- 時間データに使う TimeSlider ウィジェット
よくある落とし穴
-
reference-element がない:
<arcgis-map>タグの外に配置されたコンポーネントは、明示的な参照がないとビューを見つけられません。<arcgis-map id="myMap" basemap="topo-vector"></arcgis-map> <arcgis-search></arcgis-search><arcgis-map id="myMap" basemap="topo-vector"></arcgis-map> <arcgis-search reference-element="myMap"></arcgis-search>影響: コンポーネントは関連付けられたビューを検出できません。レンダリングされないか、エラーが発生します。
-
スロット名は厳密です:正確なスロット名(
topleftではなくtop-left)を使用してください。 -
Calcite の CSS が読み込まれない:Calcite コンポーネントを使用する前に、Calcite スクリプトが読み込まれていることを確認してください。
-
ウィジェットコンテナの競合:同じウィジェットを DOM コンテナと
view.uiの両方に追加しないでください。// Anti-pattern: widget in both container and view.ui const legend = new Legend({ view: view, container: "legendDiv" }); view.ui.add(legend, "bottom-right"); // Conflicts// Correct: pick one placement strategy const legend = new Legend({ view: view }); view.ui.add(legend, "bottom-right");影響: ウィジェットが二重にレンダリングされたり、レイアウトが崩れたりします。
-
ダーク/ライトモードの不一致:body に
calcite-mode-lightまたはcalcite-mode-darkクラスを追加してください。 -
コア API ウィジェットの CSS がない:コア API ウィジェット(Map Components ではないもの)を使用する場合は、
@arcgis/core/assets/esri/themes/light/main.cssをインポートする必要があります。Map Components は CSS を自動的に処理します。
関連スキル
- 特殊なウィジェット(BuildingExplorer、FloorFilter、Track、Locate など)については、
arcgis-widgets-advancedを参照してください - 計測、印刷、ルート検索、スワイプツールについては、
arcgis-map-toolsを参照してください - Editor および Sketch ウィジェットについては、
arcgis-editingを参照してください - FeatureTable と FeatureForm の詳細については、
arcgis-tables-formsを参照してください