99namespace Microsoft . AspNetCore . Components . Web . Media ;
1010
1111/// <summary>
12- /// Base component that handles turning a media stream into an object URL, caching and lifetime management.
13- /// Derived components provide the element tag name (e.g., img, video) and target attribute (e.g., src, href).
12+ /// Base component that handles turning a media stream into an object URL plus caching and lifetime management.
13+ /// Subclasses implement their own rendering and provide the target attribute (e.g., <c> src</c> or <c> href</c>) used
1414/// </summary>
1515public abstract partial class MediaComponentBase : IComponent , IHandleAfterRender , IAsyncDisposable
1616{
@@ -20,12 +20,12 @@ public abstract partial class MediaComponentBase : IComponent, IHandleAfterRende
2020 /// The current object URL (blob URL) assigned to the underlying element, or <c>null</c> if not yet loaded
2121 /// or if a previous load failed/was cancelled.
2222 /// </summary>
23- protected string ? _currentObjectUrl ;
23+ internal string ? _currentObjectUrl ;
2424
2525 /// <summary>
2626 /// Indicates whether the last load attempt ended in an error state for the active cache key.
2727 /// </summary>
28- protected bool _hasError ;
28+ internal bool _hasError ;
2929
3030 private bool _isDisposed ;
3131 private bool _initialized ;
@@ -35,68 +35,54 @@ public abstract partial class MediaComponentBase : IComponent, IHandleAfterRende
3535 /// The cache key associated with the currently active/most recent load operation. Used to ignore
3636 /// out-of-order JS interop responses belonging to stale operations.
3737 /// </summary>
38- protected string ? _activeCacheKey ;
38+ internal string ? _activeCacheKey ;
3939
4040 /// <summary>
4141 /// The <see cref="MediaSource"/> instance currently being processed (or <c>null</c> if none).
4242 /// </summary>
43- protected MediaSource ? _currentSource ;
43+ internal MediaSource ? _currentSource ;
4444 private CancellationTokenSource ? _loadCts ;
4545
4646 /// <summary>
4747 /// Gets a value indicating whether the component is currently loading the media content.
4848 /// True when a source has been provided, no object URL is available yet, and there is no error.
4949 /// </summary>
50- protected bool IsLoading => _currentSource != null && string . IsNullOrEmpty ( _currentObjectUrl ) && ! _hasError ;
50+ internal bool IsLoading => _currentSource != null && string . IsNullOrEmpty ( _currentObjectUrl ) && ! _hasError ;
5151
5252 /// <summary>
5353 /// Gets a value indicating whether the renderer is interactive so client-side JS interop can be performed.
5454 /// </summary>
55- protected bool IsInteractive => _renderHandle . IsInitialized && _renderHandle . RendererInfo . IsInteractive ;
55+ internal bool IsInteractive => _renderHandle . IsInitialized && _renderHandle . RendererInfo . IsInteractive ;
5656
5757 /// <summary>
5858 /// Gets the reference to the rendered HTML element for this media component.
5959 /// </summary>
60- protected ElementReference ? Element { get ; set ; }
60+ internal ElementReference ? Element { get ; set ; }
6161
6262 /// <summary>
6363 /// Gets or sets the JS runtime used for interop with the browser to materialize media object URLs.
6464 /// </summary>
65- [ Inject ] protected IJSRuntime JSRuntime { get ; set ; } = default ! ;
65+ [ Inject ] internal IJSRuntime JSRuntime { get ; set ; } = default ! ;
6666
6767 /// <summary>
6868 /// Gets or sets the logger factory used to create the <see cref="Logger"/> instance.
6969 /// </summary>
70- [ Inject ] protected ILoggerFactory LoggerFactory { get ; set ; } = default ! ;
70+ [ Inject ] internal ILoggerFactory LoggerFactory { get ; set ; } = default ! ;
7171
7272 /// <summary>
7373 /// Logger for media operations.
7474 /// </summary>
75- protected ILogger Logger => _logger ??= LoggerFactory . CreateLogger ( GetType ( ) ) ;
75+ private ILogger Logger => _logger ??= LoggerFactory . CreateLogger ( GetType ( ) ) ;
7676 private ILogger ? _logger ;
7777
78- /// <summary>
79- /// Gets the element tag name (e.g., "img", "video").
80- /// </summary>
81- protected abstract string TagName { get ; }
82-
83- /// <summary>
84- /// Gets the attribute name to assign the object URL to (e.g., "src" or "href").
85- /// </summary>
86- protected abstract string TargetAttributeName { get ; }
87-
88- /// <summary>
89- /// Gets the custom marker data-attribute name added to the rendered element for diagnostics and tests.
90- /// Derived components must override to provide a component-specific marker (e.g., "data-blazor-image").
91- /// </summary>
92- protected abstract string MarkerAttributeName { get ; }
78+ internal abstract string TargetAttributeName { get ; }
9379
9480 /// <summary>
9581 /// Determines whether the component should automatically invoke a media load after the first render
9682 /// and whenever the <see cref="Source"/> changes. Override and return <c>false</c> for components
9783 /// (such as download buttons) that defer loading until an explicit user action.
9884 /// </summary>
99- protected virtual bool ShouldAutoLoad => true ;
85+ internal virtual bool ShouldAutoLoad => true ;
10086
10187 /// <summary>
10288 /// Gets or sets the media source.
@@ -173,7 +159,7 @@ async Task IHandleAfterRender.OnAfterRenderAsync()
173159 /// Triggers a render of the component by invoking the <see cref="BuildRenderTree"/> method.
174160 /// Ensures that only one render operation is pending at a time to prevent redundant renders.
175161 /// </summary>
176- protected void Render ( )
162+ internal void Render ( )
177163 {
178164 Debug . Assert ( _renderHandle . IsInitialized ) ;
179165
@@ -185,38 +171,7 @@ protected void Render()
185171 }
186172 }
187173
188- /// <summary>
189- /// Builds the component render tree for the underlying media element and common attributes.
190- /// Derived components can override to extend the markup.
191- /// </summary>
192- /// <param name="builder">The <see cref="RenderTreeBuilder"/> used to construct the render tree.</param>
193- protected virtual void BuildRenderTree ( RenderTreeBuilder builder )
194- {
195- builder . OpenElement ( 0 , TagName ) ;
196-
197- if ( ! string . IsNullOrEmpty ( _currentObjectUrl ) )
198- {
199- builder . AddAttribute ( 1 , TargetAttributeName , _currentObjectUrl ) ;
200- }
201-
202- builder . AddAttribute ( 2 , MarkerAttributeName , "" ) ;
203-
204- var showInitial = Source != null && _currentSource == null && string . IsNullOrEmpty ( _currentObjectUrl ) && ! _hasError ;
205-
206- if ( IsLoading || showInitial )
207- {
208- builder . AddAttribute ( 3 , "data-state" , "loading" ) ;
209- }
210- else if ( _hasError )
211- {
212- builder . AddAttribute ( 3 , "data-state" , "error" ) ;
213- }
214-
215- builder . AddMultipleAttributes ( 4 , AdditionalAttributes ) ;
216- builder . AddElementReferenceCapture ( 5 , elementReference => Element = elementReference ) ;
217-
218- builder . CloseElement ( ) ;
219- }
174+ private protected virtual void BuildRenderTree ( RenderTreeBuilder builder ) { }
220175
221176 private sealed class MediaLoadResult
222177 {
@@ -309,7 +264,7 @@ public ValueTask DisposeAsync()
309264 /// <summary>
310265 /// Cancels any in-flight media load operation, if one is active, by signalling its <see cref="CancellationTokenSource"/>.
311266 /// </summary>
312- protected void CancelPreviousLoad ( )
267+ internal void CancelPreviousLoad ( )
313268 {
314269 try
315270 {
@@ -325,7 +280,7 @@ protected void CancelPreviousLoad()
325280 /// <summary>
326281 /// Creates a new <see cref="CancellationTokenSource"/> for an upcoming load operation and returns its token.
327282 /// </summary>
328- protected CancellationToken ResetCancellationToken ( )
283+ internal CancellationToken ResetCancellationToken ( )
329284 {
330285 _loadCts = new CancellationTokenSource ( ) ;
331286 return _loadCts . Token ;
0 commit comments