Codeer.LowCode.Blazor 向けの拡張フィールドライブラリ。
カレンダー、ガントチャート、カンバンボードなど、ローコードフレームワークに高機能UIコンポーネントを追加する。
NuGetパッケージ Codeer.LowCode.Blazor.Extras (MIT) として配布。
- リポジトリ: https://github.com/Codeer-Software/Codeer.LowCode.Blazor.Extras
- ターゲット: .NET 8.0
- 言語: C# (Nullable有効, ImplicitUsings有効)
- ライセンス: MIT
- 本体リポジトリ:
C:\tfs\codeer\Codeer.LowCode.Blazor(参照用、直接編集しない)
Codeer.LowCode.Blazor.Extras/
├── Source/
│ ├── Codeer.LowCode.Blazor.Extras/ # メインライブラリ (Razor Class Library)
│ │ ├── Components/ # Blazor UIコンポーネント (.razor + .razor.css)
│ │ ├── Designs/ # フィールドデザインクラス (JSON定義)
│ │ └── Fields/ # ランタイムフィールドクラス
│ └── Example/Extras/ # 動作確認用サンプルアプリ
├── Experiments/ # 実験的プロトタイプ (統合前の検証用)
│ ├── BlazorMyCalendarApp/ # カレンダーUI実験
│ └── GanttBlazor/ # ガントチャートUI実験 (SVGベース)
├── bk/ # バックアップ
│ └── GanttBlazor/ # ガントチャート旧版
└── LICENSE, README.md
<PackageReference Include="Codeer.LowCode.Blazor" Version="1.2.45" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />本体 Codeer.LowCode.Blazor はNuGetパッケージとして参照。
dotnet build Source/Codeer.LowCode.Blazor.Extras/Codeer.LowCode.Blazor.Extras.csprojGeneratePackageOnBuild: True でビルド時にNuGetパッケージを自動生成。
すべて実装済み。各フィールドのユーザー向け詳細は docs/Xxx.md を参照。
- 状態: 実装済み (docs/CalendarField.md)
- 機能: 月・週・日表示のカレンダー。イベントの表示・追加・編集、モジュールデータとの連携
- インターフェース:
IDisplayName,ISearchResultsViewFieldDesign,IFillHeightFieldDesign - ファイル:
Designs/CalendarFieldDesign.cs,Fields/CalendarField.cs,Components/CalendarFieldComponent.razor
- 状態: 実装済み (docs/GanttField.md)
- 機能: SVGベースのガントチャート。タスクのドラッグ移動・リサイズ、依存関係の管理
- インターフェース:
IDisplayName,ISearchResultsViewFieldDesign,IFillHeightFieldDesign - ファイル:
Designs/GanttFieldDesign.cs,Fields/GanttField.cs,Components/GanttFieldComponent.razor
- 状態: 実装済み (docs/TaskBoardField.md)
- 機能: カンバンボード。ドラッグ&ドロップでステータス変更
- インターフェース:
IDisplayName,ISearchResultsViewFieldDesign,IFillHeightFieldDesign - ファイル:
Designs/TaskBoardFieldDesign.cs,Fields/TaskBoardField.cs,Components/TaskBoardFieldComponent.razor
- 状態: 実装済み (docs/MarkerListField.md)
- 機能: 画像上にマーカー(ピン)を配置・操作。クリック/ダブルクリックイベント
- インターフェース:
ISearchResultsViewFieldDesign,IDataDependentField,IFillHeightFieldDesign - ファイル:
Designs/MarkerListFieldDesign.cs,Fields/MarkerListField.cs,Components/MarkerListFieldComponent.razor
- 状態: 実装済み (docs/QrCodeField.md)
- 機能: 文字列をQRコード画像として表示する表示専用フィールド。参照フィールド(
SourceField)の値、固定文字列(Text)、またはスクリプト(Field.Text)で内容を設定。ECCレベル・前景/背景色を指定可能 - 依存:
QRCoder(MIT, 純C#・System.Drawing非依存)。C#側でPNGを生成し base64 data URI で<img>表示 (JS不要) - インターフェース:
IDataDependentField(SourceField変更に自動追従) - ファイル:
Designs/QrCodeFieldDesign.cs,Fields/QrCodeField.cs,Fields/QrCodeEccLevel.cs,Components/QrCodeFieldComponent.razor
- 状態: 実装済み (docs/ProgressField.md)
- 機能: 進捗率をガント風の角丸バーで表示する表示専用フィールド。値は別フィールド(
ValueField)から、色も別フィールド(ColorField)または固定色(BarColor)から取得。進捗率をバー上に重ねて表示 (ShowValueLabelでON/OFF)、文字色は背景色に対する YIQ コントラスト色を自動選択 (Ganttと同じ)。リストに入れて各行の進捗表示に使える - インターフェース:
IDataDependentField(ValueField/ColorFieldの変更に自動追従) - ファイル:
Designs/ProgressFieldDesign.cs,Fields/ProgressField.cs,Components/ProgressFieldComponent.razor
- 状態: 実装済み (docs/RichTextField.md)
- 機能: 書式付きテキストエディタ。太字・色・リンクなどのHTMLフォーマットに対応
- ファイル:
Designs/RichTextFieldDesign.cs,Fields/RichTextField.cs,Data/RichTextFieldData.cs,Components/RichTextFieldComponent.razor
- 状態: 実装済み (docs/MarkdownField.md)
- 機能: Markdown をプレーンテキストのまま DB 列に保存し、閲覧時 (IsViewOnly) は HTML に描画。編集時はツールバー (見出し/太字/斜体/取り消し線/箇条書き/番号付き/チェックリスト/引用/リンク/コード/コードブロック/表) と
PreviewMode(Tab=タブ切替 / Split=左右並び / None。編集中だけの設定で、読取専用時は常にプレビュー表示。表示名は「プレビュー(読取専用時はプレビューで表示)」= 「なし」なのに閲覧で描画されて見える誤読への対策 2026-09-08)。MaxLength/Placeholder/ShowToolbar。必須と最大文字数の検証。高さの設定は持たない (0.9.1 で Rows 撤去。JSattach/autoSizeが中身に合わせて伸ばす=最小 6 行。FillAvailable の最終行や行 Height で外から高さが決まっているときは「textarea を 0 にしても .markdown-body が縮まない」で判定して伸ばさず stretch+内部スクロール。RichText と同じ振る舞い) - 描画:
Markdown/MarkdownRenderer(Markdig 0.37.0、AdvancedExtensions + 単独改行を<br>+DisableHtmlで生 HTML 無効化 = 信頼できないテキストを描く前提) + リンクに target=_blank。表示 CSS は.markdown-view ::deep。閲覧・プレビューで共通 - 編集の同期: textarea はローカル
_textを oninput で持ち、blur (onchange) / ツールバー操作 / タブ切替でField.SetValueAsync(キー入力ごとに OnDataChanged を起こさない)。外部からの値変更はField.OnDataChangedAsyncで_textへ戻す。ツールバーはwwwroot/markdown-interop.jsのapplyActionが選択範囲を書き換えて全文を返す - RichTextField との住み分け: 書き手が記法を知っている人か AI なら Markdown、記法を知らない利用者なら RichText (HTML 保存)
- 未対応: 検索条件 (SearchLayout)、一覧セル向けの 1 行表示、画像アップロード
- スクリプト:
Value/Html/PlainText/AppendLine(string) - ファイル:
Designs/MarkdownFieldDesign.cs(enumMarkdownPreviewMode同居),Data/MarkdownFieldData.cs,Fields/MarkdownField.cs,Components/MarkdownFieldComponent.razor(.css),Markdown/MarkdownRenderer.cs,wwwroot/markdown-interop.js
- 状態: 実装済み (docs/ColorPickerField.md)
- 機能: HTML5ネイティブカラーピッカー。色をHEX文字列として保存
- ファイル:
Designs/ColorPickerFieldDesign.cs,Fields/ColorPickerField.cs,Data/ColorPickerFieldData.cs,Components/ColorPickerFieldComponent.razor
- 状態: 実装済み (docs/AIChatField.md)。サーバー側に標準 Agent (
RawDataAccessAgent。会話エンジンChatClientAgentは internal) あり。Example の既定はダミー (Example/.../Extras.Server/AI/DummyAIChatAgent.cs)、RawDataAccessは AISettings (Azure OpenAI) があるときだけ登録 - 機能: チャット UI だけを担う。入力 (Ctrl+Enter 常に送信 / Shift+Enter 常に改行 / Enter は
SendOnEnter既定 true / IME 対応 / 自動伸長。Placeholder / WelcomeMessage は撤去 = 必要なら画面にラベルを置く。.aichat-messagesは min-height 16rem で初期表示に高さ)、考え中ドット + 途中経過 + 経過秒、逐次表示、エラー + 再送、停止、新しい会話、返事のコピー。返事は HTML をそのまま描画 (SVG グラフ含む) - Agent の選択: デザインの
Agent(文字列、空 = 既定) をAIChatSendRequest.Agentで送り、AIChatJobStoreがコンストラクタで受けた対応表Func<string, IAIChatAgent?>で解決 (アプリ側の静的クラス。Example はAI/AIChatAgentTable.cs= MailSenderTable と同型、名前ごとに Lazy で 1 つ、Jobs静的プロパティに JobStore)。無い名前は error。DI 登録なし。AIChatJobStore.Start(owner, conv, message, agentName) - 通信:
POST {EndPoint}→ 202{requestId}→GET {EndPoint}/{requestId}をポーリング (最初の 10 秒は 1 秒、以後 2.5 秒) →{status, reply, progress, error}。DELETEで中断。やり取りする型はAIChat/(AIChatSendRequest / AIChatSendResponse / AIChatStatusResponse / AIChatJobStatus) - サーバー (Extras.Server
AI/Chat/。フォルダ = 役割: 直下 = 共有の契約と窓口 (IAIChatAgent/AIChatJobStore/ChatReplyHtml)、ChatClient/= 内部の会話エンジン (ChatClientAgent とツールセット機構、Chart ツール。すべて internal)、RawDataAccess/= 専用 (Agent / Options / ToolSet / DbSchemaReader)、DesignKnowledge/= 設計参照ツール (DesignDescriber = DesignData の要約、DesignKnowledgeToolSet = list_modules / describe_module / read_document)。名前空間もフォルダどおり):IAIChatAgent/AIChatJobStore(プロセス内ジョブ。プロセスに 1 つ。対応表 Func か単一 Agent を受ける) /ChatReplyHtml(Markdig で Markdown→HTML、テキストはエスケープ、HTML 素通し + target=_blank) /ChatClientAgent(Microsoft.Extensions.AIIChatClient。システムプロンプト + internal なIAIChatToolSet群、会話履歴ConversationHistory(鍵 = 所有者+会話ID (所有者空なら会話IDのみ)。切り詰め 3 段: 古いターンのツール呼出/結果を落とす KeepToolResultsForTurns=2 → ターン数 MaxHistoryTurns=20 → 文字数 MaxHistoryCharacters=40000。いずれもターン境界で)、FunctionInvokingChatClient で往復上限、ストリーミング → ReportPartial、Markdown→HTML→ToolSet.PostProcessHtml) /RawDataAccess/RawDataAccessToolSet(get_schema =DbSchemaReaderで DB 種別ごとのカタログ問い合わせ + モジュール定義の業務名、execute_sql = 1 文 SELECT 判定Validate+ DbCommand 直叩き (タイムアウト・MaxRows・MaxResultChars) + ログ) /ChatClient/ChartToolSet+SvgChart(render_chart →[[chart:N]]プレースホルダ → 後処理で<div class="aichat-chart"><svg>) /RawDataAccessAgent(ChatClientAgent を包含して委譲 + 上 2 ツールセット。継承ではない。ChatClientAgent は sealed) - 公開面: public は契約 (
IAIChatAgent/AIChatAgentRequest/AIChatReply/AIChatReplyFormat/IAIChatProgress)、窓口 (AIChatJobStore+ Options)、標準 Agent (RawDataAccessAgent+RawDataAccessOptions。会話の設定 (プロンプト・履歴上限・逐次表示) も RawDataAccessOptions に、ILoggerFactory はコンストラクタ) だけ。ChatClientAgent/ChatClientAgentOptions/IAIChatToolSet/AIChatToolContext/RawDataAccessToolSet/ChartToolSet/SvgChart/ChatReplyHtml/ConversationHistoryは internal (テストは IVT)。独自ツールの Agent はアプリがIAIChatAgentを直接実装する (ToolSet を公開しない = 実装の詳細) - 設計と文書: 業務の意味はデザインにある。
RawDataAccessAgent(clientFactory, dbAccessorFactory, Func<DesignData?>? design, Func<IReadOnlyList<AIChatDocument>>? documents, options)。design があれば DesignKnowledgeToolSet が付き、モデルは list_modules/describe_module で業務語→表・列・候補値・リンク・スクリプトを確かめる (QueryField の SQL は .sql ファイル=実行エンジン専用のバッファなので出さない)。文書 (AIChatDocument) の出所はホスト = デザインのResources/{DocumentFolder}/*.md(フォルダはAIChatFieldDesign.DocumentFolder→AIChatSendRequest.DocumentFolder→AIChatAgentRequest.DocumentFolder。空 = 文書なし。読み出しは本体 1.3.32 のDesignDataFileManager.GetResourceTexts(dir, folder, ".md", ".txt")(App.zip の Resources/{folder} 直下) を Example / Starter の対応表が呼ぶ)。IAIChatToolSet.GetInstructions(context)(依頼ごとに文書が変わるためプロパティから引数付きに)。Example デザインにResources/AIChat/データの見方.md。文書は合計 8000 字以下なら全文をプロンプトに入れる (実 AI 検証: 一覧だけだと read_document を呼ばず「売上」を全件合計した→全文注入で定義どおり完了分のみになった)、超えたら抜粋 200 字 + read_document - 画面 URL:
DesignDescriber.PageUrlsがページフレームのサイドバーリンク (Module 一致 → PageFrame/ModuleUrlSegment) から/{frame}/{segment}と/{frame}/{segment}/{Id}を組む (リンク無しはルートのフレーム、フレーム無しは省略)。list_modules と describe_module に出し、プロンプトで「行を挙げるときは詳細リンクを付ける」。ChatReplyHtml.AddLinkTargetsは外部 (http(s)://、//) だけ target=_blank、アプリ内相対リンクは同じタブ - トークン節約 (2026-09-09): get_schema は 2 段 (引数なし = 表名+列数+モジュールの目次 /
tables指定 = その表だけtable(col TYPE [説明], …)の 1 行)。方言はRawDataAccessToolSet.GetInstructionsに常時 (Dialects()= DataSource 種別のみ・接続なし)。プロンプトで順序を明示: 文書 → describe_module (表と列が出る) → 足りないときだけ get_schema。列情報は全データソース分を 10 分キャッシュ (LoadSchemaAsync)。ツール結果は履歴で直近 2 ターン後に落ちる - 複数データソース:
RawDataAccessOptions.DataSourceNames(IList)。get_schema はデータソースごとに## データソース X+ 方言、execute_sql はdataSource引数 (1 つなら省略可)。設計の業務名は「モジュールの DataSourceName が一致するもの優先、無ければ表名で全モジュール」 - 方針 (ユーザー決定 2026-09-09): 権限管理はライブラリで持たず DB 側の設定 / 接続文字列 (AI 用読み取り専用ユーザー・列 GRANT・ビュー) で。基本的にデータは AI に渡してよい (Azure OpenAI は学習不使用・リージョン内)。気にする顧客 (個人情報・契約・業法) には列 GRANT / ビュー / 閉域・ローカルモデルの 3 手段。docs/AIChatField.md「AI に送られるデータ」に記載。除外列オプション等は要望が出てから
- 権限の考え方: RawDataAccess は DB を生で読む。見える範囲 =
RawDataAccessOptions.DataSourceNamesの接続 (AI 用の読み取り専用 DB ユーザーを GRANT で絞る) で決める。ログインユーザーごとの行制限は効かないので、置くページの UserReadCondition で使える人を絞る。SELECT 判定は補助 - IChatClient の作り方: アプリの責務 (Example
AI/AIChatAgentTable.csの CreateAzureOpenAI = AISettings の Azure OpenAI を Microsoft.Extensions.AI.OpenAI のAsIChatClient()で)。ライブラリの依存は Microsoft.Extensions.AI 10.7.0 のみ - 結線: 静的
AIChatField.EndPoint(テンプレートは "/api/ai_chat")。デスクトップはAIChatField.SendCoreAsyncフック。Example の appsettingsAIChat:RawDataAccessDataSource(既定 SampleSQLite、SystemConfig.AIChat)。テスト環境: gitignore 済appsettings.AIChatTest.json(キー入り) + プロファイル「Extras.Server (AIChatTest)」https://localhost:7469 - 会話の保持 (0.11.0): 会話は
AIChatConversation(発言・会話 ID・ポーリング) としてAIChatConversationStore(DI スコープのServicesに ConditionalWeakTable で紐づく = ホスト登録なしでスコープと同じ寿命。鍵{Module}:{Field}。静的なのは弱参照の索引だけ。★これが正式な形。Extras クライアントはホストに DI 登録を求めない = 登録必須化は既存ホストを壊す破壊的変更なのでやらない) に置き、AIChatFieldは表示だけの薄い層 (Changed/ReplyReceived の委譲、コンポーネント Dispose でDetachAsync)。ポーリングは会話側で続くので離脱中に返事が届く。sessionStorage は使わない (リロードで消えるのは意図)。KeepConversation=falseなら保管庫に置かず離脱で中断。送信時の写し (AIChatSendRequest.Transcript: テキストのみ・直近 6 往復・4000 字) はサーバー履歴が消えたときの文脈復元用で、ChatClientAgentは履歴が無いときだけConversationHistory.Seed - 複数インスタンス: ジョブと会話履歴はプロセス内 → セッション固定 (ARR アフィニティ) 前提。必要になれば DB テーブルの共有ストアに差し替える (本体の一時ファイル表方式)
- ローカライズ: 画面に出る文言 (進捗・未登録エラー・グラフの「データなし」) は Extras.Server の
Properties/Resources(.ja-JP).resx。JobStore が Start 時のカルチャをバックグラウンドへ引き継ぐ。モデル向けのプロンプトとツール説明は日本語の定数 (ローカライズ対象外) - テスト:
Test/AI/(JobStore + 対応表の振り分け / ChatClientAgent は台本つきFakeChatClient/ RawDataAccessToolSet は SQLite 実 DB / SvgChart) - インターフェース:
IFillHeightFieldDesign(Height=0 で親の高さに合わせる) - ファイル:
Designs/AIChatFieldDesign.cs,Fields/AIChatField.cs,Components/AIChatFieldComponent.razor(.css),wwwroot/aichat-interop.js,AIChat/*.cs
- 状態: 実装済み (docs/EnterFocusMoveField.md)
- 機能: モジュール内で Enter キー押下時に次の入力要素にフォーカスを移動 (末尾→先頭ループ、tabindex尊重、IME対応)
- UI: なし (デザインモードでのみ
EnterFocusMoveラベルを表示) - JS interop:
wwwroot/enterfocusmove-interop.jsでモジュールルート ([data-module]/[data-module-design]) に keydown をバインド - 除外:
<textarea>,contenteditable,data-consumes-enter属性を持つ要素, Submit ボタン - ファイル:
Designs/EnterFocusMoveFieldDesign.cs,Fields/EnterFocusMoveField.cs,Components/EnterFocusMoveFieldComponent.razor,wwwroot/enterfocusmove-interop.js
- 状態: 実装済み (Extras.Designer FieldDocs/LoginAccountContractFieldDesign.md)
- 機能: ユーザーモジュール (CurrentUserModule) に置き、行がログインアカウントとしてどう振る舞うかを宣言する契約フィールド。役割 (フィールド参照): LoginName (必須) / ExternalLoginName (外部 IdP の突き合わせ。空なら LoginName) / IsActive (偽なら拒否) / DisplayName (Cookie の Name と TOTP の QR) / TwoFactorEmail (メール二要素の送信先)。ログイン用の書き込み専用列: DbColumnPasswordHash + Salt (照合用。空 = パスワードログイン無し)、DbColumnTotp* 3 列 (認証アプリの二要素。空 = 無効)。テンプレートのログイン (Extras.Server
LoginAccountStore/TotpLogin/EmailOtpLogin) はこの契約だけを見る。PasswordHashField は「書く側」として別に置く - ファイル:
Designs/LoginAccountContractFieldDesign.cs(ContractFieldDesignBase派生。UI・データ無し)
- 状態: 実装済み (docs/TwoFactorLogin.md)。Extras.Server
Auth/EmailOtpLogin.cs(フィールドは無し) - 機能: LoginAccountContractField の TwoFactorEmail (送信先フィールド) を設定するとパスワード成功後に 6 桁コードをメールで送り入力を求める。コードはサーバーのキャッシュ (IDistributedCache) に有効期限付き。試行超過で破棄。契約の TOTP 列があれば認証アプリ優先
- 設定: appsettings
EmailOtpLogin(MailInfraName / Subject / Body / CodeLifetimeMinutes / MaxAttempts、すべて任意)
- 状態: 実装済み (Extras.Designer FieldDocs/TotpResetButtonFieldDesign.md)
- 機能: ログインユーザーモジュールの詳細画面に置き、表示中の行のユーザーの認証アプリ (TOTP) 登録を解除する。TOTP の 3 列を
DbColumn(IsWriteOnly)で持ち、解除は「3 列を空にするデータ」を通常の Submit で書く (SubmitButtonField と同じく Module.SubmitAsync)。権限は CLB の権限モデル (UserWrite / DataWrite 条件) がそのまま効くのでサーバーに専用の入口は無い。列は契約と同じでなければならない (デザインチェック)。登録済みかは表示しない (書き込み専用列は SELECT されない) - ファイル:
Designs/TotpResetButtonFieldDesign.cs,Data/TotpResetButtonFieldData.cs,Fields/TotpResetButtonField.cs,Components/TotpResetButtonFieldComponent.razor
- 状態: 実装済み (Extras.Designer FieldDocs/MyTotpResetButtonFieldDesign.md)
- 機能: ログイン中の自分の認証アプリ (TOTP) 登録を解除するボタン。表示中の行とは無関係で、どのモジュールにも置ける (設定画面・マイページ)。状態はサーバー問い合わせ (列は書き込み専用)
- 結線: 静的
TotpResetClient.StatusEndPoint/ResetEndPoint(テンプレートは api/account/totp/status, /reset。ログイン中のユーザーが対象) - ファイル:
Designs/MyTotpResetButtonFieldDesign.cs,Fields/MyTotpResetButtonField.cs,Fields/TotpResetClient.cs,Components/MyTotpResetButtonFieldComponent.razor
- 契約の
PasswordField(同モジュールの PasswordField 参照) を指定すると、PasswordHashHelper.ApplyPasswordHashが契約のデータ (LoginAccountContractFieldData= PasswordHash / PasswordSalt) を差し込み、本体が契約の書き込み専用列へ書く。ユーザーモジュールに PasswordHashField は不要 (併用はデザインチェック エラー・ヘルパーも契約を優先) - TOTP の 3 列は契約の列名プロパティだけ (DbColumn 属性なし)。本体の書き込みは「送られたフィールドの DbColumn メンバーを全部書く」ので、同じデータに載せるとパスワード保存で NULL 上書きされるため。
ContractFieldDesignBase.GetRolePropertiesは CandidateType.DbColumn のプロパティを役割から除外する
- 状態: 実装済み (docs/PasswordHashField.md)
- 機能: 平文
PasswordFieldを Submit 時にハッシュ+ソルトへ変換し、2つのDBカラムへ書き込む書き込み専用フィールド。UIなし - 要件: サーバ側で
PasswordHashHelper.ApplyPasswordHash(...)を呼ぶ実装が必須 (Helper は Extras 本体に同梱) - ファイル:
Designs/PasswordHashFieldDesign.cs,Fields/PasswordHashField.cs,Data/PasswordHashFieldData.cs,Components/PasswordHashFieldComponent.razor
- 状態: 実装済み (docs/OrientationLockField.md)
- 機能: タッチ端末で画面の向き(横/縦)が指定と異なるとき、全画面オーバーレイで回転を促す。CSSメディアクエリ (
pointer: coarse) のみで制御 (JS不要) - UI: デザインモードでは
OrientationLock (...)プレースホルダを表示 - ファイル:
Designs/OrientationLockFieldDesign.cs,Fields/OrientationLockField.cs,Components/OrientationLockFieldComponent.razor
- タスクガントチャート (
TaskGantt.razor): SVGベース、ドラッグ移動/リサイズ、タスク依存線、スナップ。→GanttFieldとして製品化済み - 設備稼働ガントチャート (
DeviceStateGantt.razor): SVGベース、ステータス色分け(正常/警告/異常)、時間軸
Extrasでは以下の3ファイル構成が基本 (データなしフィールドの場合):
| ファイル | 配置先 | 役割 |
|---|---|---|
XxxFieldDesign.cs |
Designs/ |
デザイン時定義 (JSON永続化) |
XxxField.cs |
Fields/ |
ランタイムロジック |
XxxFieldComponent.razor |
Components/ |
Blazor UI + .razor.css |
値を持つフィールドは Data/XxxFieldData.cs も必要 (4ファイル構成。データクラスは Data/ に配置)。
Codeer.LowCode.Blazor.Extras.Designs # デザインクラス
Codeer.LowCode.Blazor.Extras.Fields # ランタイムクラス
Codeer.LowCode.Blazor.Extras.Components # Blazorコンポーネント
using Codeer.LowCode.Blazor.DesignLogic.Check;
using Codeer.LowCode.Blazor.DesignLogic.Refactor;
using Codeer.LowCode.Blazor.Extras.Components;
using Codeer.LowCode.Blazor.Extras.Fields;
using Codeer.LowCode.Blazor.OperatingModel;
using Codeer.LowCode.Blazor.Repository.Data;
using Codeer.LowCode.Blazor.Repository.Design;
namespace Codeer.LowCode.Blazor.Extras.Designs
{
public class XxxFieldDesign() : FieldDesignBase(typeof(XxxFieldDesign).FullName!)
{
// デザイナーで設定可能なプロパティ
[Designer(CandidateType = CandidateType.Field)]
public string SomeField { get; set; } = string.Empty;
[Designer(CandidateType = CandidateType.ScriptEvent)]
public string OnSomeEvent { get; set; } = string.Empty;
public override string GetWebComponentTypeFullName() => typeof(XxxFieldComponent).FullName!;
public override string GetSearchWebComponentTypeFullName() => string.Empty;
public override string GetSearchControlTypeFullName() => string.Empty;
public override FieldDataBase? CreateData() => null;
public override FieldBase CreateField() => new XxxField(this);
public override List<DesignCheckInfo> CheckDesign(DesignCheckContext context)
{
var result = base.CheckDesign(context);
// フィールド参照の存在チェック
context.CheckFieldFieldExistence(Name, nameof(SomeField), SomeField).AddTo(result);
return result;
}
public override RenameResult ChangeName(RenameContext context)
=> context.Builder(base.ChangeName(context))
.AddField(SomeField, x => SomeField = x)
.Build();
}
}using Codeer.LowCode.Blazor.Extras.Designs;
using Codeer.LowCode.Blazor.OperatingModel;
using Codeer.LowCode.Blazor.Repository.Data;
using Codeer.LowCode.Blazor.Script;
namespace Codeer.LowCode.Blazor.Extras.Fields
{
public class XxxField(XxxFieldDesign design) : FieldBase<XxxFieldDesign>(design)
{
public override bool IsModified => false;
public override FieldDataBase? GetData() => null;
public override FieldSubmitData GetSubmitData() => new();
public override Task SetDataAsync(FieldDataBase? fieldDataBase) => Task.CompletedTask;
[ScriptHide]
public override async Task InitializeDataAsync(FieldDataBase? fieldDataBase)
{
// 初期化ロジック
}
}
}@using Codeer.LowCode.Blazor.Components
@using Codeer.LowCode.Blazor.Extras.Fields
@inherits FieldComponentBase<XxxField>
<div>
<!-- UI実装 -->
</div>
@code {
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
}
}FieldDesignBase # 全フィールドの基底 (データなし)
├── ValueFieldDesignBase # 値を持つフィールド
│ └── DbValueFieldDesignBase # DB列にマッピングされるフィールド
└── ListFieldDesignBase # リスト系フィールド
FieldBase : IUIContext # 全フィールドの基底
└── FieldBase<TDesign> # デザイン型付き
└── ValueFieldBase<TDesign, TData, TValue> # 値あり (Source/Current/IsModified)
ISearchResultsViewFieldDesign- 検索条件を持つフィールドデザイン (SearchCondition)ISearchResultsViewField- 検索結果を表示するフィールド (SetAdditionalConditionAsync,ReloadAsync)IDataDependentField- 他フィールドのデータに依存するフィールド (GetDependencyFields())IDisplayName- 表示名を持つフィールド
TextField, NumberField, BooleanField, DateField, DateTimeField, TimeField, SelectField, RadioGroupField, LinkField, IdField, FileField, ListField, DetailListField, TileListField, SearchField, ButtonField, SubmitButtonField, LabelField, ImageViewerField, ModuleField, ProCodeField, ExecuteSqlField, QueryField
ModuleData → Module.InitializeDataAsync() → Field.InitializeDataAsync()
→ ユーザー操作
→ GetSubmitData() → ModuleDataService.SaveAsync()
// 検索してモジュール一覧取得
var items = await this.GetChildModulesAsync(searchCondition, ModuleLayoutType.Detail, layoutName);
// 新規モジュール作成
var mod = await this.CreateChildModuleAsync(moduleName, ModuleLayoutType.Detail, layoutName);
// ダイアログ表示
var result = await mod.ShowDialogAsync("OK", "Cancel");
// 保存
await mod.SubmitAsync();[Designer] // 基本プロパティ (デザイナーUIに表示)
[Designer(Scope = DesignerScope.All)] // 全スコープで表示
[Designer(CandidateType = CandidateType.Field)] // フィールド名の候補リスト
[Designer(CandidateType = CandidateType.ScriptEvent)] // スクリプトイベント
[Designer(CandidateType = CandidateType.DetailLayout)] // DetailLayout候補
[Designer(CandidateType = CandidateType.Resource)] // リソースパス候補
[ModuleMember(Member = "...")] // どのモジュールのメンバーか
[TargetFieldType(Types = [typeof(TextFieldDesign)])] // 対象フィールド型を制限
[Layout(ModuleNameMember = "...")] // レイアウト候補のモジュール指定
[ScriptMethod(ArgumentTypes = ["string"], ArgumentNames = ["id"])] // スクリプトイベントの引数定義[ScriptHide] // スクリプトから非表示
[ScriptName("Reload")] // スクリプト内での名前 (メソッド名からAsyncを除く等)
[ScriptMethodToProperty("Value")] // 非同期メソッドをプロパティとして公開- privateフィールド:
_camelCase - publicプロパティ:
PascalCase - 非同期メソッド:
Asyncサフィックス - C#: インデント4スペース / Razor, CSS: インデント2スペース
- 改行: CRLF、末尾改行あり
- コードビハインドファイルは使わない (
.razor内の@code {}に記述) - CSSスコープファイル (
.razor.css) を各コンポーネントに配置
フィールド実装の参考にする場合、本体ソースは以下から読める:
C:\tfs\codeer\Codeer.LowCode.Blazor\Source\Codeer.LowCode.Blazor\
├── Repository\Design\ # デザインクラスの実装例
├── OperatingModel\ # ランタイムクラスの実装例
├── Components\Fields\ # Blazorコンポーネントの実装例
├── Repository\Data\ # データクラスの実装例
└── DataIO\ # FieldBase拡張メソッド (GetChildModulesAsync等)
特に参考になるファイル:
ListField系:ListFieldDesignBase.cs,ListField.cs,ListFieldComponent.razorCalendarFieldの元になったパターン:ISearchResultsViewFieldの実装
(ガントチャート / カンバンボード / リッチテキストエディタ は実装済み。「既存フィールド」参照)
- チャート/ダッシュボード (ChartField) - 各種グラフ表示
- 署名パッド (SignatureField) - 手書き署名キャプチャ
- 地図 (MapField) - 位置データ可視化