diff --git a/OpenArchival.Blazor/Components/CustomComponents/ChipContainer.razor b/OpenArchival.Blazor/Components/CustomComponents/ChipContainer.razor
new file mode 100644
index 0000000..c5ddbdb
--- /dev/null
+++ b/OpenArchival.Blazor/Components/CustomComponents/ChipContainer.razor
@@ -0,0 +1,82 @@
+@* ChipContainer.razor *@
+@typeparam T
+
+
+ @* Loop through and display each item as a chip *@
+ @foreach (var item in Items)
+ {
+
+ @DisplayFunc(item)
+
+ }
+
+ @* Render the input control provided by the consumer *@
+
+ @if (InputContent is not null)
+ {
+ @InputContent(this)
+ }
+
+
+ @SubmitButton
+
+
+@code {
+ ///
+ /// The list of items to display and manage.
+ ///
+ [Parameter]
+ public required List Items { get; set; } = new();
+
+ ///
+ /// Required for two-way binding (@bind-Items).
+ ///
+ [Parameter]
+ public EventCallback> ItemsChanged { get; set; }
+
+ ///
+ /// The RenderFragment that defines the custom input control.
+ /// The 'context' is a reference to this component instance.
+ ///
+ [Parameter]
+ public RenderFragment>? InputContent { get; set; }
+
+ [Parameter]
+ public RenderFragment SubmitButton { get; set; }
+
+ ///
+ /// A function to convert an item of type T to a string for display in the chip.
+ /// Defaults to item.ToString().
+ ///
+ [Parameter]
+ public Func DisplayFunc { get; set; } = item => item?.ToString() ?? string.Empty;
+
+
+
+ ///
+ /// A public method that the consumer's input control can call to add a new item.
+ ///
+ public async Task AddItem(T item)
+ {
+ if (item is null || (item is string str && string.IsNullOrWhiteSpace(str)))
+ {
+ return;
+ }
+
+ // Add the item if it doesn't already exist
+ if (!Items.Contains(item))
+ {
+ Items.Add(item);
+ await ItemsChanged.InvokeAsync(Items);
+ }
+ }
+
+ ///
+ /// Removes an item from the list when the chip's close icon is clicked.
+ ///
+ private async Task RemoveItem(T item)
+ {
+ Items.Remove(item);
+ await ItemsChanged.InvokeAsync(Items);
+ }
+}
\ No newline at end of file
diff --git a/OpenArchival.Blazor/Components/CustomComponents/ChipTagInput.razor b/OpenArchival.Blazor/Components/CustomComponents/ChipTagInput.razor
deleted file mode 100644
index 53b3ed2..0000000
--- a/OpenArchival.Blazor/Components/CustomComponents/ChipTagInput.razor
+++ /dev/null
@@ -1,122 +0,0 @@
-@using MudBlazor
-
-
- @* Loop through and display each tag as a chip *@
- @foreach (var tag in Items)
- {
-
@tag
- }
-
- @* Text field for adding new tags *@
-
- @switch (InputType)
- {
- case ChipTagInputType.TextBox:
- {
-
- break;
- }
- case ChipTagInputType.AutoComplete:
- {
- @if (AutocompleteSearchFunc is not null)
- {
-
-
-
- }
- break;
- }
- }
-
-
-
-@code {
- public enum ChipTagInputType
- {
- None,
- TextBox,
- AutoComplete,
- Date
- }
-
- private string _newTag = "";
-
- ///
- /// The list of tags to display and manage.
- ///
- [Parameter]
- public List Items { get; set; } = new();
-
- ///
- /// Required for two-way binding (@bind-Items).
- ///
- [Parameter]
- public EventCallback> ItemsChanged { get; set; }
-
- [Parameter]
- public EventCallback OnChanged { get; set; }
-
- [Parameter]
- public string Placeholder { get; set; } = "Add tag...";
-
- [Parameter]
- public ChipTagInputType InputType { get; set; } = ChipTagInputType.TextBox;
-
- [Parameter]
- public Func>>? AutocompleteSearchFunc { get; set; } = null;
-
- private MudTextField? _mudTextField;
-
- private MudAutocomplete? _mudAutoComplete;
-
- ///
- /// Handles the key press event in the text field.
- ///
- private async Task HandleKeyDownTextBox(KeyboardEventArgs e)
- {
- if (e.Key == "Enter" && !string.IsNullOrWhiteSpace(_newTag))
- {
- // Add the tag if it doesn't already exist
- if (!Items.Contains(_newTag, StringComparer.OrdinalIgnoreCase))
- {
- Items.Add(_newTag);
- await ItemsChanged.InvokeAsync(Items);
- await OnChanged.InvokeAsync();
- }
-
- // Clear the input field
- _newTag = "";
-
- if (_mudTextField is not null)
- _mudTextField.Clear();
-
- if (_mudAutoComplete is not null)
- await _mudAutoComplete.ClearAsync();
- }
- }
-
- ///
- /// Removes a tag from the list when the close icon is clicked.
- ///
- private async Task RemoveTag(string tag)
- {
- Items.Remove(tag);
- await ItemsChanged.InvokeAsync(Items);
- await OnChanged.InvokeAsync();
- }
-}
\ No newline at end of file
diff --git a/OpenArchival.Blazor/Components/CustomComponents/FileUploadOptions.cs b/OpenArchival.Blazor/Components/CustomComponents/FileUploadOptions.cs
new file mode 100644
index 0000000..6ac9a6a
--- /dev/null
+++ b/OpenArchival.Blazor/Components/CustomComponents/FileUploadOptions.cs
@@ -0,0 +1,10 @@
+namespace OpenArchival.Blazor.Components;
+
+public class FileUploadOptions
+{
+ public static string Key = "FileUploadOptions";
+ public required long MaxUploadSizeBytes { get; set; }
+ public required string UploadFolderPath { get; set; }
+
+ public required int MaxFileCount { get; set; }
+}
diff --git a/OpenArchival.Blazor/Components/CustomComponents/UploadDropBox.razor b/OpenArchival.Blazor/Components/CustomComponents/UploadDropBox.razor
index cb740cc..81954e2 100644
--- a/OpenArchival.Blazor/Components/CustomComponents/UploadDropBox.razor
+++ b/OpenArchival.Blazor/Components/CustomComponents/UploadDropBox.razor
@@ -1,4 +1,5 @@
-@inject ISnackbar Snackbar
+@using Microsoft.Extensions.Options
+TODO: Handle the case in which there are duplicate file names